diff --git a/doc/api/nova-task.api b/doc/api/nova-task.api index 925ae73..b6f2f01 100644 --- a/doc/api/nova-task.api +++ b/doc/api/nova-task.api @@ -20,22 +20,22 @@ service novatask { } type GetTaskListReq { - CommunityId uint `form:"community_id"` + CommunityId uint `form:"community_id,optional"` // 所属社区ID } type Task { - Id uint `json:"id"` - CommunityId uint `json:"community_id"` - Title string `json:"title"` - SubTitle string `json:"sub_title"` - Description string `json:"description"` - Points int `json:"points"` - ButtonText string `json:"button_text"` - Type int8 `json:"type"` - Url string `json:"url"` - Status int8 `json:"status"` - StartAt string `json:"start_at"` - EndAt string `json:"end_at"` + Id uint `json:"id"` // 任务ID + CommunityId uint `json:"community_id"` // 所属社区ID + Title string `json:"title"` // 任务标题 + SubTitle string `json:"sub_title"` // 副标题 + Description string `json:"description"` // 任务描述 + Points int `json:"points"` // 任务积分 + ButtonText string `json:"button_text"` // 按钮文字 + Type int8 `json:"type"` // 任务类型: 0=follow_twitter,1=bind_twitter,2=cast_twitter,3=publish_twitter,4=repost_twitter,5=watch_youtube,6=follow_youtube,7=bind_discord,8=join_telegram,9=daily_pay + Url string `json:"url"` // 跳转链接 + Status int8 `json:"status"` // 任务状态: 0=不启用,1=启用 + StartAt string `json:"start_at"` // 开始时间 + EndAt string `json:"end_at"` // 结束时间 FinishState int8 `json:"finish_state"` // 0:未完成 1:待校验 2:已完成未领取 3:已领取 } @@ -44,14 +44,14 @@ type GetTaskListResp { } type TaskIdPath { - ID uint `path:"id"` + ID uint `path:"id"` // 任务ID } type VerifyTaskResultResp { - Finish bool `json:"finish"` + Finish bool `json:"finish"` // 是否完成 } type GetTaskRewardResp { - Points int `json:"points"` + Points int `json:"points"` // 积分 } diff --git a/doc/swagger/nova-task.json b/doc/swagger/nova-task.json index 834f5a9..e02b82e 100644 --- a/doc/swagger/nova-task.json +++ b/doc/swagger/nova-task.json @@ -90,8 +90,9 @@ "parameters": [ { "name": "community_id", + "description": " 所属社区ID", "in": "query", - "required": true, + "required": false, "type": "integer", "format": "uint32" } @@ -116,13 +117,11 @@ "properties": { "community_id": { "type": "integer", - "format": "uint32" + "format": "uint32", + "description": " 所属社区ID" } }, - "title": "GetTaskListReq", - "required": [ - "community_id" - ] + "title": "GetTaskListReq" }, "GetTaskListResp": { "type": "object", @@ -144,7 +143,8 @@ "properties": { "points": { "type": "integer", - "format": "int32" + "format": "int32", + "description": " 积分" } }, "title": "GetTaskRewardResp", @@ -157,44 +157,56 @@ "properties": { "id": { "type": "integer", - "format": "uint32" + "format": "uint32", + "description": " 任务ID" }, "community_id": { "type": "integer", - "format": "uint32" + "format": "uint32", + "description": " 所属社区ID" }, "title": { - "type": "string" + "type": "string", + "description": " 任务标题" }, "sub_title": { - "type": "string" + "type": "string", + "description": " 副标题" }, "description": { - "type": "string" + "type": "string", + "description": " 任务描述" }, "points": { "type": "integer", - "format": "int32" + "format": "int32", + "description": " 任务积分" }, "button_text": { - "type": "string" + "type": "string", + "description": " 按钮文字" }, "type": { "type": "integer", - "format": "int8" + "format": "int8", + "description": " 任务类型: 0=follow_twitter,1=bind_twitter,2=cast_twitter,3=publish_twitter,4=repost_twitter,5=watch_youtube,6=follow_youtube,7=bind_discord,8=join_telegram,9=daily_pay" }, "url": { - "type": "string" + "type": "string", + "description": " 跳转链接" }, "status": { "type": "integer", - "format": "int8" + "format": "int8", + "description": " 任务状态: 0=不启用,1=启用" }, "start_at": { - "type": "string" + "type": "string", + "description": " 开始时间" }, "end_at": { - "type": "string" + "type": "string", + "description": " 结束时间" }, "finish_state": { "type": "integer", @@ -228,7 +240,8 @@ "properties": { "finish": { "type": "boolean", - "format": "boolean" + "format": "boolean", + "description": " 是否完成" } }, "title": "VerifyTaskResultResp", diff --git a/etc/novatask.yaml b/etc/novatask.yaml index 8dba4c0..0372d2e 100644 --- a/etc/novatask.yaml +++ b/etc/novatask.yaml @@ -1,13 +1,20 @@ Name: novatask Host: 0.0.0.0 -Port: 8888 +Port: 443 +CertFile: "etc/cert/saas.crt" +KeyFile: "etc/cert/saas.key" Auth: # js-sdk鉴权相关配置 - AccessSecret: "ac2d27613e131be6286c0eb1713929dd" # 鉴权token密钥 + AccessSecret: "Mj2G%szYe&$MP@ytNv8JktQN1n5^cPq%" # 鉴权token密钥 AccessExpire: 168h # 鉴权token过期时间 MySql: # mysql相关配置 - Addr: "127.0.0.1:3306" # mysql地址 - User: "root" # mysql用户 - Password: "123456" # mysql密码 - Database: "nova_home" # 数据库名 \ No newline at end of file + Addr: "192.168.2.108:3306" # mysql地址 + User: "huangjie" # mysql用户 + Password: "jMDqPQM^a6hsAR" # mysql密码 + Database: "nova_home" # 数据库名 + +Earn: + ClientId: "4d6269e3-8aac-4550-acf9-dc891caf20a8" + ClientSecret: "GJpQ4TmX4p2VMY7U3XtExZQKYfibMv24" + GameId: "c0deda99-bb15-47a2-a3be-f1fe2983cde2" diff --git a/go.mod b/go.mod index 2930af5..b17c2fd 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,11 @@ module nova_task go 1.23.4 require ( + github.com/earn-alliance/earnalliance-go v0.0.2 github.com/golang-jwt/jwt/v4 v4.5.1 github.com/shopspring/decimal v1.4.0 github.com/spf13/cast v1.7.0 + github.com/stretchr/testify v1.9.0 github.com/zeromicro/go-zero v1.7.4 ) @@ -14,18 +16,22 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.18.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect @@ -51,4 +57,5 @@ require ( google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ca0949e..03c2f8f 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/earn-alliance/earnalliance-go v0.0.2 h1:cKxc+lEn6vj+qgoIkwUjeW3nLmghb86ZGtBs5Saokwc= +github.com/earn-alliance/earnalliance-go v0.0.2/go.mod h1:mN3wHms0bjW4SQ15smJoXbwCc2Aj/Yeij+Bma7LgvMQ= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -32,6 +34,12 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= +github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -76,6 +84,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= diff --git a/internal/config/config.go b/internal/config/config.go index 1dbed85..352377f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -5,6 +5,8 @@ import ( "github.com/zeromicro/go-zero/core/stores/sqlx" "github.com/zeromicro/go-zero/rest" "net/url" + "nova_task/internal/pkg/earn" + "time" ) type Config struct { @@ -12,8 +14,9 @@ type Config struct { MySql MySqlConf Auth struct { AccessSecret string - AccessExpire int64 + AccessExpire time.Duration } + Earn earn.Config } // MySqlConf mysql配置 diff --git a/internal/logic/task/get_task_list_logic.go b/internal/logic/task/get_task_list_logic.go index 3443fde..3cf8ef8 100644 --- a/internal/logic/task/get_task_list_logic.go +++ b/internal/logic/task/get_task_list_logic.go @@ -29,8 +29,7 @@ func NewGetTaskListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTa } func (l *GetTaskListLogic) GetTaskList(req *types.GetTaskListReq) (*types.GetTaskListResp, error) { - uid := cast.ToInt(l.ctx.Value("uid")) - + uid := cast.ToStringMapInt(l.ctx.Value("data"))["id"] tasks, err := l.svcCtx.TaskModel.FindTasksByCommunity(l.ctx, req.CommunityId) if err != nil { l.Errorw("get task list failed", logx.Field("err", err), logx.Field("communityId", req.CommunityId)) diff --git a/internal/logic/task/get_task_reward_logic.go b/internal/logic/task/get_task_reward_logic.go index ac0aa8c..14c92ed 100644 --- a/internal/logic/task/get_task_reward_logic.go +++ b/internal/logic/task/get_task_reward_logic.go @@ -4,6 +4,7 @@ import ( "context" "errors" "github.com/spf13/cast" + "github.com/zeromicro/go-zero/core/stores/sqlx" "nova_task/internal/model" "nova_task/internal/pkg/errs" "time" @@ -29,8 +30,8 @@ func NewGetTaskRewardLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get } } -func (l *GetTaskRewardLogic) GetTaskReward(req *types.TaskIdPath) (resp *types.GetTaskRewardResp, err error) { - uid := cast.ToInt(l.ctx.Value("uid")) +func (l *GetTaskRewardLogic) GetTaskReward(req *types.TaskIdPath) (*types.GetTaskRewardResp, error) { + uid := cast.ToStringMapInt(l.ctx.Value("data"))["id"] task, err := l.svcCtx.TaskModel.FindOne(l.ctx, req.ID) if err != nil { if errors.Is(err, model.ErrNotFound) { @@ -57,22 +58,35 @@ func (l *GetTaskRewardLogic) GetTaskReward(req *types.TaskIdPath) (resp *types.G return nil, errs.BadRequest(errs.ErrTaskAlreadyReward, "task already reward") } - // 给予用户奖励 - err = l.svcCtx.TaskAssetModel.AddUserPoint(l.ctx, uid, task.Points) + // 修改状态,增加积分和记录都在事物中执行 + err = l.svcCtx.DBConn.TransactCtx(l.ctx, func(ctx context.Context, session sqlx.Session) error { + // 修改状态 + tp.Stage = model.TASK_PROGRESS_REWARDED + err = l.svcCtx.TaskProgressModel.WithSession(session).Update(l.ctx, tp) + if err != nil { + return err + } + // 给予用户奖励 + err = l.svcCtx.TaskAssetModel.WithSession(session).AddUserPoint(l.ctx, uid, task.Points) + if err != nil { + return err + } + // 记录用户获奖记录 + _, err = l.svcCtx.TaskAssetRecordModel.Insert(l.ctx, &model.NhTaskAssetRecord{ + Uid: uid, + EventId: uint64(task.Id), + AssetField: "points", + Count: float64(task.Points), + Remark: "完成任务:" + task.Title, + CreateTime: int(time.Now().Unix()), + }) + return err + }) + if err != nil { - l.Errorw("add user point error", logx.Field("err", err)) + l.Errorw("给予用户奖励实物执行失败", logx.Field("err", err), logx.Field("task", task.Id), logx.Field("uid", uid)) return nil, errs.InternalServer(errs.ErrDatabaseOperate, err) } - // 记录用户获奖记录 - _, err = l.svcCtx.TaskAssetRecordModel.Insert(l.ctx, &model.NhTaskAssetRecord{ - Uid: uid, - EventId: uint64(task.Id), - AssetField: "points", - Count: float64(task.Points), - Remark: task.Title, - CreateTime: int(time.Now().Unix()), - }) - - return + return &types.GetTaskRewardResp{Points: task.Points}, nil } diff --git a/internal/logic/task/verify_task_result_logic.go b/internal/logic/task/verify_task_result_logic.go index 45c3519..6e8ec2d 100644 --- a/internal/logic/task/verify_task_result_logic.go +++ b/internal/logic/task/verify_task_result_logic.go @@ -28,7 +28,7 @@ func NewVerifyTaskResultLogic(ctx context.Context, svcCtx *svc.ServiceContext) * } func (l *VerifyTaskResultLogic) VerifyTaskResult(req *types.TaskIdPath) (*types.VerifyTaskResultResp, error) { - uid := cast.ToInt(l.ctx.Value("uid")) + uid := cast.ToStringMapInt(l.ctx.Value("data"))["id"] task, err := l.svcCtx.TaskModel.FindOne(l.ctx, req.ID) if err != nil { if errors.Is(err, model.ErrNotFound) { @@ -59,6 +59,17 @@ func (l *VerifyTaskResultLogic) VerifyTaskResult(req *types.TaskIdPath) (*types. // todo: 校验用户是否完成该任务 switch task.Type { case model.TASKTYPE_BIND_TWITTER: + tw, err := l.svcCtx.TwitterModel.FindOneByUid(l.ctx, uint(uid)) + if err != nil { + if !errors.Is(err, model.ErrNotFound) { + return nil, errs.InternalServer(errs.ErrDatabaseOperate, err) + } + return &types.VerifyTaskResultResp{Finish: false}, nil + } + if tw.TwitterId == "" { + return &types.VerifyTaskResultResp{Finish: false}, nil + } + case model.TASKTYPE_BIND_DISCORD: case model.TASKTYPE_DAILY_PAY: default: diff --git a/internal/model/nh_task_asset_model.go b/internal/model/nh_task_asset_model.go index 3c50cd5..aadd5d0 100755 --- a/internal/model/nh_task_asset_model.go +++ b/internal/model/nh_task_asset_model.go @@ -14,7 +14,7 @@ type ( // and implement the added methods in customNhTaskAssetModel. NhTaskAssetModel interface { nhTaskAssetModel - withSession(session sqlx.Session) NhTaskAssetModel + WithSession(session sqlx.Session) NhTaskAssetModel AddUserPoint(ctx context.Context, uid int, points int) error } @@ -30,7 +30,7 @@ func NewNhTaskAssetModel(conn sqlx.SqlConn) NhTaskAssetModel { } } -func (m *customNhTaskAssetModel) withSession(session sqlx.Session) NhTaskAssetModel { +func (m *customNhTaskAssetModel) WithSession(session sqlx.Session) NhTaskAssetModel { return NewNhTaskAssetModel(sqlx.NewSqlConnFromSession(session)) } diff --git a/internal/model/nh_task_asset_record_model.go b/internal/model/nh_task_asset_record_model.go index 0d20cf1..43fe711 100755 --- a/internal/model/nh_task_asset_record_model.go +++ b/internal/model/nh_task_asset_record_model.go @@ -1,6 +1,13 @@ package model -import "github.com/zeromicro/go-zero/core/stores/sqlx" +import ( + "context" + "database/sql" + "fmt" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "github.com/zeromicro/go-zero/core/stringx" + "strings" +) var _ NhTaskAssetRecordModel = (*customNhTaskAssetRecordModel)(nil) @@ -27,3 +34,10 @@ func NewNhTaskAssetRecordModel(conn sqlx.SqlConn) NhTaskAssetRecordModel { func (m *customNhTaskAssetRecordModel) withSession(session sqlx.Session) NhTaskAssetRecordModel { return NewNhTaskAssetRecordModel(sqlx.NewSqlConnFromSession(session)) } + +func (m *customNhTaskAssetRecordModel) Insert(ctx context.Context, data *NhTaskAssetRecord) (sql.Result, error) { + rows := strings.Join(stringx.Remove(nhTaskAssetRecordFieldNames, "`id`"), ",") + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?)", m.table, rows) + ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.EventId, data.AssetField, data.Count, data.Remark, data.ProvideUid, data.CreateTime) + return ret, err +} diff --git a/internal/model/nh_task_model.go b/internal/model/nh_task_model.go index 1ec7f53..d0f2897 100755 --- a/internal/model/nh_task_model.go +++ b/internal/model/nh_task_model.go @@ -38,7 +38,7 @@ type ( ) func (m *customNhTaskModel) FindTasksByCommunity(ctx context.Context, communityId uint) ([]*NhTask, error) { - query := fmt.Sprintf("select %s from %s where community_id = ?", nhTaskRows, m.table) + query := fmt.Sprintf("select %s from %s where community_id = 0 or community_id = ?", nhTaskRows, m.table) var tasks []*NhTask err := m.conn.QueryRowsCtx(ctx, &tasks, query, communityId) if err != nil && !errors.Is(err, sqlx.ErrNotFound) { diff --git a/internal/model/nh_task_progress_model.go b/internal/model/nh_task_progress_model.go index f57750a..3268876 100755 --- a/internal/model/nh_task_progress_model.go +++ b/internal/model/nh_task_progress_model.go @@ -17,7 +17,7 @@ type ( // and implement the added methods in customNhTaskProgressModel. NhTaskProgressModel interface { nhTaskProgressModel - withSession(session sqlx.Session) NhTaskProgressModel + WithSession(session sqlx.Session) NhTaskProgressModel } customNhTaskProgressModel struct { @@ -32,6 +32,6 @@ func NewNhTaskProgressModel(conn sqlx.SqlConn) NhTaskProgressModel { } } -func (m *customNhTaskProgressModel) withSession(session sqlx.Session) NhTaskProgressModel { +func (m *customNhTaskProgressModel) WithSession(session sqlx.Session) NhTaskProgressModel { return NewNhTaskProgressModel(sqlx.NewSqlConnFromSession(session)) } diff --git a/internal/model/nh_twitter_model.go b/internal/model/nh_twitter_model.go new file mode 100755 index 0000000..a2e4e54 --- /dev/null +++ b/internal/model/nh_twitter_model.go @@ -0,0 +1,29 @@ +package model + +import "github.com/zeromicro/go-zero/core/stores/sqlx" + +var _ NhTwitterModel = (*customNhTwitterModel)(nil) + +type ( + // NhTwitterModel is an interface to be customized, add more methods here, + // and implement the added methods in customNhTwitterModel. + NhTwitterModel interface { + nhTwitterModel + withSession(session sqlx.Session) NhTwitterModel + } + + customNhTwitterModel struct { + *defaultNhTwitterModel + } +) + +// NewNhTwitterModel returns a model for the database table. +func NewNhTwitterModel(conn sqlx.SqlConn) NhTwitterModel { + return &customNhTwitterModel{ + defaultNhTwitterModel: newNhTwitterModel(conn), + } +} + +func (m *customNhTwitterModel) withSession(session sqlx.Session) NhTwitterModel { + return NewNhTwitterModel(sqlx.NewSqlConnFromSession(session)) +} diff --git a/internal/model/nh_twitter_model_gen.go b/internal/model/nh_twitter_model_gen.go new file mode 100755 index 0000000..cc210ae --- /dev/null +++ b/internal/model/nh_twitter_model_gen.go @@ -0,0 +1,121 @@ +// 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 ( + nhTwitterFieldNames = builder.RawFieldNames(&NhTwitter{}) + nhTwitterRows = strings.Join(nhTwitterFieldNames, ",") + nhTwitterRowsExpectAutoSet = strings.Join(stringx.Remove(nhTwitterFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",") + nhTwitterRowsWithPlaceHolder = strings.Join(stringx.Remove(nhTwitterFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?" +) + +type ( + nhTwitterModel interface { + Insert(ctx context.Context, data *NhTwitter) (sql.Result, error) + FindOne(ctx context.Context, id uint) (*NhTwitter, error) + FindOneByTwitterId(ctx context.Context, twitterId string) (*NhTwitter, error) + FindOneByUid(ctx context.Context, uid uint) (*NhTwitter, error) + Update(ctx context.Context, data *NhTwitter) error + Delete(ctx context.Context, id uint) error + } + + defaultNhTwitterModel struct { + conn sqlx.SqlConn + table string + } + + NhTwitter struct { + Id uint `db:"id"` + Uid uint `db:"uid"` // 用户ID + TwitterId string `db:"twitter_id"` // twitter_id + Name string `db:"name"` // name + Username string `db:"username"` // username + CreatedAt time.Time `db:"created_at"` // 创建时间 + UpdatedAt time.Time `db:"updated_at"` // 修改时间 + } +) + +func newNhTwitterModel(conn sqlx.SqlConn) *defaultNhTwitterModel { + return &defaultNhTwitterModel{ + conn: conn, + table: "`nh_twitter`", + } +} + +func (m *defaultNhTwitterModel) 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 *defaultNhTwitterModel) FindOne(ctx context.Context, id uint) (*NhTwitter, error) { + query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", nhTwitterRows, m.table) + var resp NhTwitter + 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 *defaultNhTwitterModel) FindOneByTwitterId(ctx context.Context, twitterId string) (*NhTwitter, error) { + var resp NhTwitter + query := fmt.Sprintf("select %s from %s where `twitter_id` = ? limit 1", nhTwitterRows, m.table) + err := m.conn.QueryRowCtx(ctx, &resp, query, twitterId) + switch err { + case nil: + return &resp, nil + case sqlx.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *defaultNhTwitterModel) FindOneByUid(ctx context.Context, uid uint) (*NhTwitter, error) { + var resp NhTwitter + query := fmt.Sprintf("select %s from %s where `uid` = ? limit 1", nhTwitterRows, 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 *defaultNhTwitterModel) Insert(ctx context.Context, data *NhTwitter) (sql.Result, error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?)", m.table, nhTwitterRowsExpectAutoSet) + ret, err := m.conn.ExecCtx(ctx, query, data.Uid, data.TwitterId, data.Name, data.Username) + return ret, err +} + +func (m *defaultNhTwitterModel) Update(ctx context.Context, newData *NhTwitter) error { + query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, nhTwitterRowsWithPlaceHolder) + _, err := m.conn.ExecCtx(ctx, query, newData.Uid, newData.TwitterId, newData.Name, newData.Username, newData.Id) + return err +} + +func (m *defaultNhTwitterModel) tableName() string { + return m.table +} diff --git a/internal/pkg/earn/earn.go b/internal/pkg/earn/earn.go new file mode 100644 index 0000000..b1fb718 --- /dev/null +++ b/internal/pkg/earn/earn.go @@ -0,0 +1,45 @@ +package earn + +import ( + ea "github.com/earn-alliance/earnalliance-go" + "time" +) + +type Config struct { + ClientId string + ClientSecret string + GameId string + Dsn string `json:",optional"` + FlushCooldown time.Duration `json:",optional"` + FlushInterval time.Duration `json:",optional"` + BatchSize int `json:",optional"` +} + +func (c Config) BuildEarnClient() *ea.Client { + clientBuilder := ea.NewClientBuilder(). + WithClientID(c.ClientId). + WithClientSecret(c.ClientSecret). + WithGameID(c.GameId) + + if c.Dsn != "" { + clientBuilder = clientBuilder.WithDSN(c.Dsn) + } + + if c.FlushCooldown > 0 { + clientBuilder = clientBuilder.WithFlushCooldown(c.FlushCooldown) + } + + if c.FlushInterval > 0 { + clientBuilder = clientBuilder.WithFlushInterval(c.FlushInterval) + } + + if c.BatchSize > 0 { + clientBuilder = clientBuilder.WithBatchSize(c.BatchSize) + } + + return clientBuilder.Build() +} + +const ( + EVENT_BIND_ROLE = "BIND_ROLE" +) diff --git a/internal/pkg/earn/earn_test.go b/internal/pkg/earn/earn_test.go new file mode 100644 index 0000000..291616b --- /dev/null +++ b/internal/pkg/earn/earn_test.go @@ -0,0 +1,33 @@ +package earn + +import ( + ea "github.com/earn-alliance/earnalliance-go" + "github.com/stretchr/testify/require" + "testing" + "time" +) + +func TestEarn(t *testing.T) { + clientID := "4d6269e3-8aac-4550-acf9-dc891caf20a8" + clientSecret := "GJpQ4TmX4p2VMY7U3XtExZQKYfibMv24" + gameID := "c0deda99-bb15-47a2-a3be-f1fe2983cde2" + c := Config{ + ClientId: clientID, + ClientSecret: clientSecret, + GameId: gameID, + FlushCooldown: time.Second, + } + e := c.BuildEarnClient() + userId := "8006586979" + e.SetIdentifiers(userId, &ea.Identifiers{ + Email: ea.IdentifierFrom("test-mail@earn.com"), + EpicGamesID: ea.IdentifierFrom("78657657"), + //WalletAddress: ea.IdentifierFrom("0x0769d094"), + }) + e.StartGame(userId) + e.Track(userId, "TEST_EVENT", ea.PointerFrom(100), map[string]any{"level": 88}) + time.Sleep(time.Second * 2) + err := e.Flush() + require.Nil(t, err) + e.Close() +} diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 789bb8c..8e489d7 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -1,6 +1,9 @@ package svc import ( + ea "github.com/earn-alliance/earnalliance-go" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/sqlx" "nova_task/internal/config" "nova_task/internal/model" ) @@ -11,6 +14,9 @@ type ServiceContext struct { TaskAssetModel model.NhTaskAssetModel TaskAssetRecordModel model.NhTaskAssetRecordModel TaskProgressModel model.NhTaskProgressModel + TwitterModel model.NhTwitterModel + Earn *ea.Client + DBConn sqlx.SqlConn } func NewServiceContext(c config.Config) *ServiceContext { @@ -21,5 +27,16 @@ func NewServiceContext(c config.Config) *ServiceContext { TaskAssetModel: model.NewNhTaskAssetModel(dbConn), TaskAssetRecordModel: model.NewNhTaskAssetRecordModel(dbConn), TaskProgressModel: model.NewNhTaskProgressModel(dbConn), + TwitterModel: model.NewNhTwitterModel(dbConn), + Earn: c.Earn.BuildEarnClient(), + DBConn: dbConn, } } + +func (s *ServiceContext) Close() { + err := s.Earn.Flush() + if err != nil { + logx.Errorw("flush earn error", logx.Field("err", err)) + } + s.Earn.Close() +} diff --git a/internal/types/types.go b/internal/types/types.go index b7238d0..6f8734b 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -4,7 +4,7 @@ package types type GetTaskListReq struct { - CommunityId uint `form:"community_id"` + CommunityId uint `form:"community_id,optional"` // 所属社区ID } type GetTaskListResp struct { @@ -12,29 +12,29 @@ type GetTaskListResp struct { } type GetTaskRewardResp struct { - Points int `json:"points"` + Points int `json:"points"` // 积分 } type Task struct { - Id uint `json:"id"` - CommunityId uint `json:"community_id"` - Title string `json:"title"` - SubTitle string `json:"sub_title"` - Description string `json:"description"` - Points int `json:"points"` - ButtonText string `json:"button_text"` - Type int8 `json:"type"` - Url string `json:"url"` - Status int8 `json:"status"` - StartAt string `json:"start_at"` - EndAt string `json:"end_at"` + Id uint `json:"id"` // 任务ID + CommunityId uint `json:"community_id"` // 所属社区ID + Title string `json:"title"` // 任务标题 + SubTitle string `json:"sub_title"` // 副标题 + Description string `json:"description"` // 任务描述 + Points int `json:"points"` // 任务积分 + ButtonText string `json:"button_text"` // 按钮文字 + Type int8 `json:"type"` // 任务类型: 0=follow_twitter,1=bind_twitter,2=cast_twitter,3=publish_twitter,4=repost_twitter,5=watch_youtube,6=follow_youtube,7=bind_discord,8=join_telegram,9=daily_pay + Url string `json:"url"` // 跳转链接 + Status int8 `json:"status"` // 任务状态: 0=不启用,1=启用 + StartAt string `json:"start_at"` // 开始时间 + EndAt string `json:"end_at"` // 结束时间 FinishState int8 `json:"finish_state"` // 0:未完成 1:待校验 2:已完成未领取 3:已领取 } type TaskIdPath struct { - ID uint `path:"id"` + ID uint `path:"id"` // 任务ID } type VerifyTaskResultResp struct { - Finish bool `json:"finish"` + Finish bool `json:"finish"` // 是否完成 } diff --git a/novatask.go b/novatask.go index 30949a6..21c1ed5 100644 --- a/novatask.go +++ b/novatask.go @@ -25,6 +25,8 @@ func main() { defer server.Stop() ctx := svc.NewServiceContext(c) + defer ctx.Close() + handler.RegisterHandlers(server, ctx) fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)