完善任务接口逻辑以及Eran事件上报接入

This commit is contained in:
lianghuanjie
2024-12-13 16:15:20 +08:00
parent 9f4fb0a9d0
commit f9084a0eb3
20 changed files with 408 additions and 84 deletions

View File

@@ -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配置

View File

@@ -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))

View File

@@ -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
}

View File

@@ -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:

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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))
}

View File

@@ -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))
}

View File

@@ -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
}

45
internal/pkg/earn/earn.go Normal file
View File

@@ -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"
)

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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"` // 是否完成
}