diff --git a/internal/handler/carv/bind_wallet_handler.go b/internal/handler/carv/bind_wallet_handler.go index bc8c96d..f7f1875 100644 --- a/internal/handler/carv/bind_wallet_handler.go +++ b/internal/handler/carv/bind_wallet_handler.go @@ -2,6 +2,7 @@ package carv import ( "net/http" + "nova_task/internal/pkg/errs" "github.com/zeromicro/go-zero/rest/httpx" "nova_task/internal/logic/carv" @@ -12,18 +13,15 @@ import ( // 注册绑定钱包任务 func BindWalletHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var req types.EmailKey if err := httpx.Parse(r, &req); err != nil { - httpx.ErrorCtx(r.Context(), w, err) + httpx.ErrorCtx(ctx, w, err) return } - l := carv.NewBindWalletLogic(r.Context(), svcCtx) - resp, err := l.BindWallet(&req) - if err != nil { - httpx.ErrorCtx(r.Context(), w, err) - } else { - httpx.OkJsonCtx(r.Context(), w, resp) - } + l := carv.NewBindWalletLogic(ctx, svcCtx) + resp := l.BindWallet(&req) + errs.WriteHttpResponse(ctx, w, resp) } } diff --git a/internal/handler/carv/download_and_bind_role_handler.go b/internal/handler/carv/download_and_bind_role_handler.go index 216a0dd..44988b8 100644 --- a/internal/handler/carv/download_and_bind_role_handler.go +++ b/internal/handler/carv/download_and_bind_role_handler.go @@ -2,6 +2,7 @@ package carv import ( "net/http" + "nova_task/internal/pkg/errs" "github.com/zeromicro/go-zero/rest/httpx" "nova_task/internal/logic/carv" @@ -13,17 +14,14 @@ import ( func DownloadAndBindRoleHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.EmailKey + ctx := r.Context() if err := httpx.Parse(r, &req); err != nil { - httpx.ErrorCtx(r.Context(), w, err) + httpx.ErrorCtx(ctx, w, err) return } - l := carv.NewDownloadAndBindRoleLogic(r.Context(), svcCtx) - resp, err := l.DownloadAndBindRole(&req) - if err != nil { - httpx.ErrorCtx(r.Context(), w, err) - } else { - httpx.OkJsonCtx(r.Context(), w, resp) - } + l := carv.NewDownloadAndBindRoleLogic(ctx, svcCtx) + resp := l.DownloadAndBindRole(&req) + errs.WriteHttpResponse(ctx, w, resp) } } diff --git a/internal/handler/carv/unlock_chapter_handler.go b/internal/handler/carv/unlock_chapter_handler.go index b246b44..fe342d3 100644 --- a/internal/handler/carv/unlock_chapter_handler.go +++ b/internal/handler/carv/unlock_chapter_handler.go @@ -2,6 +2,7 @@ package carv import ( "net/http" + "nova_task/internal/pkg/errs" "github.com/zeromicro/go-zero/rest/httpx" "nova_task/internal/logic/carv" @@ -13,17 +14,14 @@ import ( func UnlockChapterHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.UnlockChapterReq + ctx := r.Context() if err := httpx.Parse(r, &req); err != nil { - httpx.ErrorCtx(r.Context(), w, err) + httpx.ErrorCtx(ctx, w, err) return } - l := carv.NewUnlockChapterLogic(r.Context(), svcCtx) - resp, err := l.UnlockChapter(&req) - if err != nil { - httpx.ErrorCtx(r.Context(), w, err) - } else { - httpx.OkJsonCtx(r.Context(), w, resp) - } + l := carv.NewUnlockChapterLogic(ctx, svcCtx) + resp := l.UnlockChapter(&req) + errs.WriteHttpResponse(ctx, w, resp) } } diff --git a/internal/handler/carv/wallet_check_in_handler.go b/internal/handler/carv/wallet_check_in_handler.go index e8aa295..c3aded9 100644 --- a/internal/handler/carv/wallet_check_in_handler.go +++ b/internal/handler/carv/wallet_check_in_handler.go @@ -2,6 +2,7 @@ package carv import ( "net/http" + "nova_task/internal/pkg/errs" "github.com/zeromicro/go-zero/rest/httpx" "nova_task/internal/logic/carv" @@ -13,17 +14,14 @@ import ( func WalletCheckInHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.EmailKey + ctx := r.Context() if err := httpx.Parse(r, &req); err != nil { - httpx.ErrorCtx(r.Context(), w, err) + httpx.ErrorCtx(ctx, w, err) return } - l := carv.NewWalletCheckInLogic(r.Context(), svcCtx) - resp, err := l.WalletCheckIn(&req) - if err != nil { - httpx.ErrorCtx(r.Context(), w, err) - } else { - httpx.OkJsonCtx(r.Context(), w, resp) - } + l := carv.NewWalletCheckInLogic(ctx, svcCtx) + resp := l.WalletCheckIn(&req) + errs.WriteHttpResponse(ctx, w, resp) } } diff --git a/internal/logic/carv/bind_wallet_logic.go b/internal/logic/carv/bind_wallet_logic.go index a4b552d..8d3cc5c 100644 --- a/internal/logic/carv/bind_wallet_logic.go +++ b/internal/logic/carv/bind_wallet_logic.go @@ -27,10 +27,10 @@ func NewBindWalletLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BindWa } } -func (l *BindWalletLogic) BindWallet(req *types.EmailKey) (*types.CarvResult, error) { - uid, errResult := l.svcCtx.FindUserByEmail(l.ctx, req.Email) +func (l *BindWalletLogic) BindWallet(req *types.EmailKey) *types.CarvResult { + uid, errResult := l.svcCtx.GetUidByEmail(l.ctx, req.Email) if errResult != nil { - return errResult, nil + return errResult } _, err := l.svcCtx.WalletModel.FindAddressByUid(l.ctx, uid) @@ -41,13 +41,13 @@ func (l *BindWalletLogic) BindWallet(req *types.EmailKey) (*types.CarvResult, er Code: int(errs.ErrDatabaseOperate), Message: "system error", }, - }, nil + } } return &types.CarvResult{ Result: &types.Result{IsValid: false}, - }, nil + } } return &types.CarvResult{ Result: &types.Result{IsValid: true}, - }, nil + } } diff --git a/internal/logic/carv/download_and_bind_role_logic.go b/internal/logic/carv/download_and_bind_role_logic.go index 375d282..54de8f7 100644 --- a/internal/logic/carv/download_and_bind_role_logic.go +++ b/internal/logic/carv/download_and_bind_role_logic.go @@ -26,10 +26,10 @@ func NewDownloadAndBindRoleLogic(ctx context.Context, svcCtx *svc.ServiceContext } } -func (l *DownloadAndBindRoleLogic) DownloadAndBindRole(req *types.EmailKey) (*types.CarvResult, error) { - uid, errResult := l.svcCtx.FindUserByEmail(l.ctx, req.Email) +func (l *DownloadAndBindRoleLogic) DownloadAndBindRole(req *types.EmailKey) *types.CarvResult { + uid, errResult := l.svcCtx.GetUidByEmail(l.ctx, req.Email) if errResult != nil { - return errResult, nil + return errResult } pb, err := l.svcCtx.PromoteBindModel.FindOneByInvitedUid(l.ctx, uid) @@ -38,20 +38,20 @@ func (l *DownloadAndBindRoleLogic) DownloadAndBindRole(req *types.EmailKey) (*ty return &types.CarvResult{Error: &types.Error{ Code: int(errs.ErrDatabaseOperate), Message: "system error", - }}, nil + }} } return &types.CarvResult{Result: &types.Result{ IsValid: false, - }}, nil + }} } if pb.IsCreateRole == 0 { return &types.CarvResult{Result: &types.Result{ IsValid: false, - }}, nil + }} } return &types.CarvResult{ Result: &types.Result{IsValid: true}, - }, nil + } } diff --git a/internal/logic/carv/unlock_chapter_logic.go b/internal/logic/carv/unlock_chapter_logic.go index b8a9c6b..bb5de79 100644 --- a/internal/logic/carv/unlock_chapter_logic.go +++ b/internal/logic/carv/unlock_chapter_logic.go @@ -2,6 +2,9 @@ package carv import ( "context" + "errors" + "nova_task/internal/model" + "nova_task/internal/pkg/errs" "nova_task/internal/svc" "nova_task/internal/types" @@ -24,8 +27,25 @@ func NewUnlockChapterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Unl } } -func (l *UnlockChapterLogic) UnlockChapter(req *types.UnlockChapterReq) (resp *types.CarvResult, err error) { - // todo: add your logic here and delete this line - - return +func (l *UnlockChapterLogic) UnlockChapter(req *types.UnlockChapterReq) *types.CarvResult { + uid, errResult := l.svcCtx.GetUidByEmail(l.ctx, req.Email) + if errResult != nil { + return errResult + } + gp, err := l.svcCtx.GameReportModel.FindOneByUid(l.ctx, uid) + if err != nil { + if !errors.Is(err, model.ErrNotFound) { + return &types.CarvResult{ + Error: &types.Error{ + Code: int(errs.ErrDatabaseOperate), + Message: "system error", + }, + } + } + return &types.CarvResult{Result: &types.Result{IsValid: false}} + } + if gp.Chapter >= req.Chapter { + return &types.CarvResult{Result: &types.Result{IsValid: true}} + } + return &types.CarvResult{Result: &types.Result{IsValid: false}} } diff --git a/internal/logic/carv/wallet_check_in_logic.go b/internal/logic/carv/wallet_check_in_logic.go index cd61926..9a8583c 100644 --- a/internal/logic/carv/wallet_check_in_logic.go +++ b/internal/logic/carv/wallet_check_in_logic.go @@ -29,10 +29,10 @@ func NewWalletCheckInLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Wal } } -func (l *WalletCheckInLogic) WalletCheckIn(req *types.EmailKey) (*types.CarvResult, error) { - uid, errResult := l.svcCtx.FindUserByEmail(l.ctx, req.Email) +func (l *WalletCheckInLogic) WalletCheckIn(req *types.EmailKey) *types.CarvResult { + uid, errResult := l.svcCtx.GetUidByEmail(l.ctx, req.Email) if errResult != nil { - return errResult, nil + return errResult } task, err := l.svcCtx.TaskModel.FindDailyPayTask(l.ctx) @@ -43,14 +43,14 @@ func (l *WalletCheckInLogic) WalletCheckIn(req *types.EmailKey) (*types.CarvResu Code: int(errs.ErrDatabaseOperate), Message: "system error", }, - }, nil + } } return &types.CarvResult{ Error: &types.Error{ Code: int(errs.ErrTaskNotFound), Message: "task not exist", }, - }, nil + } } taskSeq := cast.ToInt(time.Now().Format("20060102")) tp, err := l.svcCtx.TaskProgressModel.FindOneByUidTaskIdTaskSeq(l.ctx, int(uid), task.Id, taskSeq) @@ -61,19 +61,19 @@ func (l *WalletCheckInLogic) WalletCheckIn(req *types.EmailKey) (*types.CarvResu Code: int(errs.ErrDatabaseOperate), Message: "system error", }, - }, nil + } } return &types.CarvResult{ Result: &types.Result{IsValid: false}, - }, nil + } } if tp.Stage >= model.TASK_PROGRESS_WAIT_REWARD { return &types.CarvResult{ Result: &types.Result{IsValid: true}, - }, nil + } } return &types.CarvResult{ Result: &types.Result{IsValid: false}, - }, nil + } } diff --git a/internal/middleware/apikeycheck_middleware.go b/internal/middleware/apikeycheck_middleware.go index 66dfaf1..c95601a 100644 --- a/internal/middleware/apikeycheck_middleware.go +++ b/internal/middleware/apikeycheck_middleware.go @@ -4,6 +4,8 @@ import ( "errors" "net/http" "nova_task/internal/model" + "nova_task/internal/pkg/errs" + "nova_task/internal/types" ) type ApiKeyCheckMiddleware struct { @@ -17,16 +19,29 @@ func NewApiKeyCheckMiddleware(conf model.NhSystemConfigModel) *ApiKeyCheckMiddle func (m *ApiKeyCheckMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - key, err := m.conf.GetCarvApiKey(r.Context()) + ctx := r.Context() + key, err := m.conf.GetCarvApiKey(ctx) if err != nil { if !errors.Is(err, model.ErrNotFound) { - http.Error(w, "system error", http.StatusInternalServerError) + result := types.CarvResult{ + Error: &types.Error{ + Code: int(errs.ErrDatabaseOperate), + Message: "api key config not exist", + }, + } + errs.WriteHttpResponse(ctx, w, result) return } } apiKey := r.Header.Get("x-api-key") if apiKey == "" || apiKey != key { - http.Error(w, "Invalid API key", http.StatusUnauthorized) + result := types.CarvResult{ + Error: &types.Error{ + Code: int(errs.ErrInvalidApiKey), + Message: "invalid api key", + }, + } + errs.WriteHttpResponse(ctx, w, result) return } diff --git a/internal/model/nh_game_report_model.go b/internal/model/nh_game_report_model.go new file mode 100755 index 0000000..b678588 --- /dev/null +++ b/internal/model/nh_game_report_model.go @@ -0,0 +1,29 @@ +package model + +import "github.com/zeromicro/go-zero/core/stores/sqlx" + +var _ NhGameReportModel = (*customNhGameReportModel)(nil) + +type ( + // NhGameReportModel is an interface to be customized, add more methods here, + // and implement the added methods in customNhGameReportModel. + NhGameReportModel interface { + nhGameReportModel + withSession(session sqlx.Session) NhGameReportModel + } + + customNhGameReportModel struct { + *defaultNhGameReportModel + } +) + +// NewNhGameReportModel returns a model for the database table. +func NewNhGameReportModel(conn sqlx.SqlConn) NhGameReportModel { + return &customNhGameReportModel{ + defaultNhGameReportModel: newNhGameReportModel(conn), + } +} + +func (m *customNhGameReportModel) withSession(session sqlx.Session) NhGameReportModel { + return NewNhGameReportModel(sqlx.NewSqlConnFromSession(session)) +} diff --git a/internal/model/nh_game_report_model_gen.go b/internal/model/nh_game_report_model_gen.go new file mode 100755 index 0000000..73352d2 --- /dev/null +++ b/internal/model/nh_game_report_model_gen.go @@ -0,0 +1,107 @@ +// Code generated by goctl. DO NOT EDIT. +// versions: +// goctl version: 1.7.3 + +package model + +import ( + "context" + "database/sql" + "fmt" + "strings" + "time" + + "github.com/zeromicro/go-zero/core/stores/builder" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" +) + +var ( + nhGameReportFieldNames = builder.RawFieldNames(&NhGameReport{}) + nhGameReportRows = strings.Join(nhGameReportFieldNames, ",") + nhGameReportRowsExpectAutoSet = strings.Join(stringx.Remove(nhGameReportFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",") + nhGameReportRowsWithPlaceHolder = strings.Join(stringx.Remove(nhGameReportFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?" +) + +type ( + nhGameReportModel interface { + Insert(ctx context.Context, data *NhGameReport) (sql.Result, error) + FindOne(ctx context.Context, id uint) (*NhGameReport, error) + FindOneByUid(ctx context.Context, uid uint) (*NhGameReport, error) + Update(ctx context.Context, data *NhGameReport) error + Delete(ctx context.Context, id uint) error + } + + defaultNhGameReportModel struct { + conn sqlx.SqlConn + table string + } + + NhGameReport struct { + Id uint `db:"id"` + Uid uint `db:"uid"` // 用户ID + IsHaveTwoHero int8 `db:"is_have_two_hero"` // 是否拥有两个以上的英雄,1=是 + IsUsedSummon int8 `db:"is_used_summon"` // 是否已使用了召唤,1=是 + IsHaveHero31 int `db:"is_have_hero_31"` // 是否拥有31级以上的英雄,1=是 + Chapter int `db:"chapter"` // 完成的章节 + CreatedAt time.Time `db:"created_at"` // 创建时间 + UpdatedAt time.Time `db:"updated_at"` // 修改时间 + } +) + +func newNhGameReportModel(conn sqlx.SqlConn) *defaultNhGameReportModel { + return &defaultNhGameReportModel{ + conn: conn, + table: "`nh_game_report`", + } +} + +func (m *defaultNhGameReportModel) Delete(ctx context.Context, id uint) error { + query := fmt.Sprintf("delete from %s where `id` = ?", m.table) + _, err := m.conn.ExecCtx(ctx, query, id) + return err +} + +func (m *defaultNhGameReportModel) FindOne(ctx context.Context, id uint) (*NhGameReport, error) { + query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", nhGameReportRows, m.table) + var resp NhGameReport + err := m.conn.QueryRowCtx(ctx, &resp, query, id) + switch err { + case nil: + return &resp, nil + case sqlx.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultNhGameReportModel) FindOneByUid(ctx context.Context, uid uint) (*NhGameReport, error) { + var resp NhGameReport + query := fmt.Sprintf("select %s from %s where `uid` = ? limit 1", nhGameReportRows, m.table) + err := m.conn.QueryRowCtx(ctx, &resp, query, uid) + switch err { + case nil: + return &resp, nil + case sqlx.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultNhGameReportModel) Insert(ctx context.Context, data *NhGameReport) (sql.Result, error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, nhGameReportRowsExpectAutoSet) + ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.IsHaveTwoHero, data.IsUsedSummon, data.IsHaveHero31, data.Chapter) + return ret, err +} + +func (m *defaultNhGameReportModel) Update(ctx context.Context, newData *NhGameReport) error { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, nhGameReportRowsWithPlaceHolder) + _, err := m.conn.ExecCtx(ctx, query, newData.Uid, newData.IsHaveTwoHero, newData.IsUsedSummon, newData.IsHaveHero31, newData.Chapter, newData.Id) + return err +} + +func (m *defaultNhGameReportModel) tableName() string { + return m.table +} diff --git a/internal/pkg/errs/http_respone.go b/internal/pkg/errs/http_respone.go index 0162d6e..fbb1c16 100644 --- a/internal/pkg/errs/http_respone.go +++ b/internal/pkg/errs/http_respone.go @@ -3,12 +3,15 @@ package errs import ( "context" "encoding/json" + "errors" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/rest" "github.com/zeromicro/go-zero/rest/httpx" "net/http" ) +const JsonContentType = "application/json; charset=utf-8" + func init() { httpx.SetErrorHandlerCtx(ErrorHandleCtx) httpx.SetErrorHandler(ErrorHandle) @@ -72,3 +75,24 @@ func ErrorHandleCtx(ctx context.Context, err error) (int, any) { } return http.StatusOK, body } + +func WriteHttpResponse(ctx context.Context, w http.ResponseWriter, v any) { + w.Header().Set(httpx.ContentType, JsonContentType) + w.WriteHeader(http.StatusOK) + + body, err := json.Marshal(v) + if err != nil { + logx.WithContext(ctx).Errorw("error marshal response", logx.Field("error", err), logx.Field("value", v)) + return + } + + if n, err := w.Write(body); err != nil { + // http.ErrHandlerTimeout has been handled by http.TimeoutHandler, + // so it's ignored here. + if !errors.Is(err, http.ErrHandlerTimeout) { + logx.WithContext(ctx).Errorw("write response failed, error", logx.Field("error", err), logx.Field("value", v)) + } + } else if n < len(body) { + logx.WithContext(ctx).Errorf("actual bytes: %d, written bytes: %d", len(body), n) + } +} diff --git a/internal/pkg/errs/reason.go b/internal/pkg/errs/reason.go index fa12d5c..350ae90 100644 --- a/internal/pkg/errs/reason.go +++ b/internal/pkg/errs/reason.go @@ -27,4 +27,5 @@ const ( ErrTaskConfNotSet Reason = 20006 // 任务配置未设置 ErrUserNotFound Reason = 20007 // 用户不存在 ErrNftNotBelongToUser Reason = 20008 // NFT不属于用户 + ErrInvalidApiKey Reason = 20009 // 无效的api key ) diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 219b0a2..d8a07fe 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -39,6 +39,7 @@ type ServiceContext struct { StakePropertyModel model.NhNftStakePropertyModel EmailRewardModel model.NhEmailRewardModel AmbassadorModel model.NhTaskAmbassadorModel + GameReportModel model.NhGameReportModel ApiKeyCheck rest.Middleware AdminSecretCheck rest.Middleware @@ -74,6 +75,7 @@ func NewServiceContext(c config.Config) *ServiceContext { StakePropertyModel: model.NewNhNftStakePropertyModel(dbConn), EmailRewardModel: model.NewNhEmailRewardModel(dbConn), AmbassadorModel: model.NewNhTaskAmbassadorModel(dbConn), + GameReportModel: model.NewNhGameReportModel(dbConn), ApiKeyCheck: middleware.NewApiKeyCheckMiddleware(configModel).Handle, AdminSecretCheck: middleware.NewAdminSecretCheckMiddleware(configModel).Handle, @@ -121,7 +123,7 @@ func (s *ServiceContext) HasBindTwitter(ctx context.Context, uid uint) bool { return tw.TwitterId != "" } -func (s *ServiceContext) FindUserByEmail(ctx context.Context, email string) (uint, *types.CarvResult) { +func (s *ServiceContext) GetUidByEmail(ctx context.Context, email string) (uint, *types.CarvResult) { u, err := s.UserModel.FindOneByEmail(ctx, email) if err != nil { if !errors.Is(err, model.ErrNotFound) {