diff --git a/doc/api/transfercastile.api b/doc/api/transfercastile.api index 2bc907f..97bb630 100644 --- a/doc/api/transfercastile.api +++ b/doc/api/transfercastile.api @@ -17,26 +17,22 @@ service novatask { type TransferCastileToGameReq { RoleID int64 `json:"role_id"` // 角色id - Amount int64 `json:"amount"` // 数量 -} - -type TransferCastileToGameResp { - TransferToGameLog + Amount int64 `json:"amount,range=[1:]"` // 数量 } type TransferCastileToGameListReq { RoleID int64 `json:"role_id,optional"` // 角色id } -type TransferToGameLog { - Id int `json:"id"` // id - RoleID int64 `json:"role_id"` // 角色id - Amount int64 `json:"amount"` // 数量 - Status string `json:"elf_name"` // 状态 +type TransferCastileToGameResp { + Id int `json:"id"` // id + RoleID int64 `json:"role_id"` // 角色id + Amount int64 `json:"amount"` // 数量 + Status int64 `json:"status"` // 状态 } type TransferCastileToGameListResp { - Total int `json:"total"` // 总数 - List []TransferToGameLog `json:"list"` // 列表 + Total int `json:"total"` // 总数 + List []TransferCastileToGameResp `json:"list"` // 列表 } diff --git a/doc/swagger/nova.json b/doc/swagger/nova.json index 43290f1..4ee0212 100644 --- a/doc/swagger/nova.json +++ b/doc/swagger/nova.json @@ -1945,7 +1945,7 @@ "list": { "type": "array", "items": { - "$ref": "#/definitions/TransferToGameLog" + "$ref": "#/definitions/TransferCastileToGameResp" }, "description": " 列表" } @@ -1994,42 +1994,18 @@ "format": "int64", "description": " 数量" }, - "elf_name": { - "type": "string", + "status": { + "type": "integer", + "format": "int64", "description": " 状态" } }, - "title": "TransferCastileToGameResp" - }, - "TransferToGameLog": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int32", - "description": " id" - }, - "role_id": { - "type": "integer", - "format": "int64", - "description": " 角色id" - }, - "amount": { - "type": "integer", - "format": "int64", - "description": " 数量" - }, - "elf_name": { - "type": "string", - "description": " 状态" - } - }, - "title": "TransferToGameLog", + "title": "TransferCastileToGameResp", "required": [ "id", "role_id", "amount", - "elf_name" + "status" ] }, "UnStakeNftReq": { diff --git a/internal/consts/game_action.go b/internal/consts/game_action.go index c540cc9..639d850 100644 --- a/internal/consts/game_action.go +++ b/internal/consts/game_action.go @@ -3,4 +3,5 @@ package consts const ( GameActionGetHomePointsState = "getStakeHomePointsState" GameActionStakePoints = "stakeHomePoints" + GameActionSendNoticeAward = "SendNoticeAward" //给多角色发邮件 ) diff --git a/internal/handler/transfercastile/transfer_castile_to_game_handler.go b/internal/handler/transfercastile/transfer_castile_to_game_handler.go index c124e11..4ae8cf3 100644 --- a/internal/handler/transfercastile/transfer_castile_to_game_handler.go +++ b/internal/handler/transfercastile/transfer_castile_to_game_handler.go @@ -1,7 +1,11 @@ package transfercastile import ( + "errors" + "fmt" "net/http" + "nova_task/internal/pkg/utils" + "time" "github.com/zeromicro/go-zero/rest/httpx" "nova_task/internal/logic/transfercastile" @@ -18,8 +22,22 @@ func TransferCastileToGameHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { return } + uid := utils.GetUidUint(r.Context()) + redisKey := fmt.Sprintf("transferCastile:uid:%d", uid) + //加锁 + lock, err := svcCtx.Redis.SetnxEx(redisKey, time.Now().String(), 120) + if !lock || err != nil { + httpx.ErrorCtx(r.Context(), w, errors.New("operation frequently")) + return + } + l := transfercastile.NewTransferCastileToGameLogic(r.Context(), svcCtx) + resp, err := l.TransferCastileToGame(&req) + + //删除redis KEY + _, _ = svcCtx.Redis.Del(redisKey) + if err != nil { httpx.ErrorCtx(r.Context(), w, err) } else { diff --git a/internal/logic/transfercastile/transfer_castile_to_game_logic.go b/internal/logic/transfercastile/transfer_castile_to_game_logic.go index bf1bcc3..f6b63f2 100644 --- a/internal/logic/transfercastile/transfer_castile_to_game_logic.go +++ b/internal/logic/transfercastile/transfer_castile_to_game_logic.go @@ -2,11 +2,16 @@ package transfercastile import ( "context" - + "errors" + "github.com/zeromicro/go-zero/core/jsonx" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/sqlx" + "nova_task/internal/consts" + "nova_task/internal/model" + "nova_task/internal/pkg/errs" + "nova_task/internal/pkg/utils" "nova_task/internal/svc" "nova_task/internal/types" - - "github.com/zeromicro/go-zero/core/logx" ) type TransferCastileToGameLogic struct { @@ -25,7 +30,117 @@ func NewTransferCastileToGameLogic(ctx context.Context, svcCtx *svc.ServiceConte } func (l *TransferCastileToGameLogic) TransferCastileToGame(req *types.TransferCastileToGameReq) (resp *types.TransferCastileToGameResp, err error) { - // todo: add your logic here and delete this line - return + //获取JWT中的UID + uid := utils.GetUidUint(l.ctx) + + //验证是否当前用户的角色 + r, err := l.svcCtx.RoleModel.FindOneByRoleId(l.ctx, req.RoleID) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errs.New(errs.ErrRoleNotFound, "role not exist") + } + l.Errorw("find role error", logx.Field("err", err), logx.Field("role_id", req.RoleID), logx.Field("uid", uid)) + return nil, errs.New(errs.ErrDatabaseOperate, err) + } + u, err := l.svcCtx.UserModel.FindOneByEmail(l.ctx, r.Account) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errs.New(errs.ErrUserNotFound, "user not found") + } + l.Errorw("find user error", logx.Field("err", err), logx.Field("role_id", req.RoleID), logx.Field("uid", uid)) + return nil, errs.New(errs.ErrDatabaseOperate, err) + } + if u.Id != uid { + return nil, errs.New(errs.ErrRoleNotFound, "role not exist.") + } + + cToken, err := l.svcCtx.CastileTokenModel.FindOneByEmail(l.ctx, r.Account) + if err != nil { + if errors.Is(err, model.ErrNotFound) { + return nil, errs.New(errs.ErrPointLevelConfigNotFound, "castile record is not exists") + } + return nil, errs.New(errs.ErrDatabaseOperate, err) + } + + //判断余额 + remain := int(cToken.Total) - int(cToken.Transfer) - int(req.Amount) + if remain < 0 { + return nil, errs.New(errs.ErrInsufficientCastile, "castile insufficient") + } + + lastId := int64(0) + + err = l.svcCtx.DBConn.TransactCtx(l.ctx, func(ctx context.Context, session sqlx.Session) error { + + err := l.svcCtx.CastileTokenModel.WithSession(session).UpdateTransferIncrease(ctx, uint(req.Amount), cToken.Id) + if err != nil { + logx.Errorw("castile 余额更新失败", logx.Field("err", err), logx.Field("id", cToken.Id), logx.Field("roleId", req.RoleID)) + return err + } + + res, err := l.svcCtx.CastileTokenLogModel.WithSession(session).Insert(ctx, &model.NhCastileTokenLog{ + Uid: u.Id, + RoleId: uint64(req.RoleID), + Amount: uint(req.Amount), + Action: 1, + }) + if err != nil { + logx.Errorw("castile 日志插入失败", logx.Field("err", err), logx.Field("id", cToken.Id), logx.Field("roleId", req.RoleID)) + return err + } + + lastId, err = res.LastInsertId() + if err != nil { + return err + } + return nil + }) + + if err != nil { + logx.Errorw("castile 更新的事务报错", logx.Field("err", err), logx.Field("id", cToken.Id), logx.Field("roleId", req.RoleID)) + return nil, errs.New(errs.ErrDatabaseOperate, err) + } + + itemList := []map[string]any{ + { + "itemId": 32110, + "itemCount": 20, + }, + } + data, _ := jsonx.MarshalToString(itemList) + + //给多角色发邮件 + _, err = l.svcCtx.GameAction(l.ctx, int64(req.RoleID), consts.GameActionSendNoticeAward, map[string]any{ + "role_ids": req.RoleID, + "item_list": data, + "template_id": 120338, + "mail_ttl": 24 * 90, + }) + + if err != nil { + logx.Errorw("game 发送邮件失败", logx.Field("err", err), logx.Field("roleId", req.RoleID)) + return nil, errs.New(errs.ErrInternalServer, err) + } + + res, err := l.svcCtx.CastileTokenLogModel.FindOne(l.ctx, uint(lastId)) + if err != nil { + logx.Errorw("查询 castile 日志表出错", logx.Field("err", err), logx.Field("Id", lastId)) + return nil, errs.New(errs.ErrInternalServer, err) + } + + //更新状态 + res.CallbackStatus = 1 + err = l.svcCtx.CastileTokenLogModel.Update(l.ctx, res) + if err != nil { + logx.Errorw("更新 castile 日志表状态出错", logx.Field("err", err), logx.Field("Id", lastId)) + return nil, errs.New(errs.ErrInternalServer, err) + } + + return &types.TransferCastileToGameResp{ + Id: int(res.Id), + RoleID: int64(res.RoleId), + Amount: int64(res.Amount), + Status: int64(res.CallbackStatus), + }, nil } diff --git a/internal/model/nh_castile_token_log_model.go b/internal/model/nh_castile_token_log_model.go index 4d23712..62c7937 100644 --- a/internal/model/nh_castile_token_log_model.go +++ b/internal/model/nh_castile_token_log_model.go @@ -1,6 +1,10 @@ package model -import "github.com/zeromicro/go-zero/core/stores/sqlx" +import ( + "context" + "fmt" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) var _ NhCastileTokenLogModel = (*customNhCastileTokenLogModel)(nil) @@ -9,7 +13,8 @@ type ( // and implement the added methods in customNhCastileTokenLogModel. NhCastileTokenLogModel interface { nhCastileTokenLogModel - withSession(session sqlx.Session) NhCastileTokenLogModel + WithSession(session sqlx.Session) NhCastileTokenLogModel + UpdateStatus(ctx context.Context, status uint, id uint) error } customNhCastileTokenLogModel struct { @@ -24,6 +29,12 @@ func NewNhCastileTokenLogModel(conn sqlx.SqlConn) NhCastileTokenLogModel { } } -func (m *customNhCastileTokenLogModel) withSession(session sqlx.Session) NhCastileTokenLogModel { +func (m *customNhCastileTokenLogModel) WithSession(session sqlx.Session) NhCastileTokenLogModel { return NewNhCastileTokenLogModel(sqlx.NewSqlConnFromSession(session)) } + +func (m *customNhCastileTokenLogModel) UpdateStatus(ctx context.Context, status uint, id uint) error { + query := fmt.Sprintf("update %s set `callback_status` = ? where `id` = ?", m.table) + _, err := m.conn.ExecCtx(ctx, query, status, id) + return err +} diff --git a/internal/model/nh_castile_token_model.go b/internal/model/nh_castile_token_model.go index e919dac..400d61d 100644 --- a/internal/model/nh_castile_token_model.go +++ b/internal/model/nh_castile_token_model.go @@ -1,6 +1,10 @@ package model -import "github.com/zeromicro/go-zero/core/stores/sqlx" +import ( + "context" + "fmt" + "github.com/zeromicro/go-zero/core/stores/sqlx" +) var _ NhCastileTokenModel = (*customNhCastileTokenModel)(nil) @@ -9,7 +13,8 @@ type ( // and implement the added methods in customNhCastileTokenModel. NhCastileTokenModel interface { nhCastileTokenModel - withSession(session sqlx.Session) NhCastileTokenModel + WithSession(session sqlx.Session) NhCastileTokenModel + UpdateTransferIncrease(ctx context.Context, reduce uint, id uint) error } customNhCastileTokenModel struct { @@ -24,6 +29,12 @@ func NewNhCastileTokenModel(conn sqlx.SqlConn) NhCastileTokenModel { } } -func (m *customNhCastileTokenModel) withSession(session sqlx.Session) NhCastileTokenModel { +func (m *customNhCastileTokenModel) WithSession(session sqlx.Session) NhCastileTokenModel { return NewNhCastileTokenModel(sqlx.NewSqlConnFromSession(session)) } + +func (m *customNhCastileTokenModel) UpdateTransferIncrease(ctx context.Context, num uint, id uint) error { + query := fmt.Sprintf("update %s set `transfer` = `transfer` + ? where `id` = ?", m.table) + _, err := m.conn.ExecCtx(ctx, query, num, id) + return err +} diff --git a/internal/pkg/errs/reason.go b/internal/pkg/errs/reason.go index 4ffc800..a010182 100644 --- a/internal/pkg/errs/reason.go +++ b/internal/pkg/errs/reason.go @@ -10,14 +10,15 @@ const ( ErrOverload Reason = 10403 // 请求超载 // ======= 服务器内部错误:1000~9999 ======= - ErrInternalServer Reason = 1000 // 未知的服务器内部错误 - ErrDatabaseOperate Reason = 1001 // 数据库错误 - ErrRedisOperate Reason = 1002 // redis错误 - ErrEncodePassword Reason = 1003 // 密码加密错误 - ErrGenerateUUid Reason = 1004 // 生成uuid错误 - ErrGenerateToken Reason = 1005 // 生成token错误 - ErrSystemConfig Reason = 1006 // 系统配置错误 - GameServerError Reason = 1007 // 游戏服务器错误 + ErrInternalServer Reason = 1000 // 未知的服务器内部错误 + ErrDatabaseOperate Reason = 1001 // 数据库错误 + ErrRedisOperate Reason = 1002 // redis错误 + ErrEncodePassword Reason = 1003 // 密码加密错误 + ErrGenerateUUid Reason = 1004 // 生成uuid错误 + ErrGenerateToken Reason = 1005 // 生成token错误 + ErrSystemConfig Reason = 1006 // 系统配置错误 + GameServerError Reason = 1007 // 游戏服务器错误 + ErrOperationFrequently Reason = 1008 // 游戏服务器错误 // ======= 业务层错误:20000~29999 ======= ErrUnknownLogicError Reason = 20000 // 未知的业务错误 diff --git a/internal/svc/game_action.go b/internal/svc/game_action.go index e3213f5..5bab4e2 100644 --- a/internal/svc/game_action.go +++ b/internal/svc/game_action.go @@ -98,6 +98,7 @@ func (s *ServiceContext) GameAction(ctx context.Context, roleId int64, action st s.ErrLog(ctx, action, addr, serverInfo, request, resp) return resp, fmt.Errorf("game action msg= %s, data=%v", cast.ToString(resp["msg"]), cast.ToString(resp["data"])) } + s.ErrLog(ctx, action, addr, serverInfo, request, resp) return resp, nil } diff --git a/internal/svc/model.go b/internal/svc/model.go index 5e4aeb0..2f0b43d 100644 --- a/internal/svc/model.go +++ b/internal/svc/model.go @@ -44,6 +44,9 @@ type dbModel struct { StakePointConfigModel model.NhStakeHomePointConfigModel StakePointsModel model.NhStakePointsModel StakePointsLogModel model.NhStakePointsLogModel + + CastileTokenModel model.NhCastileTokenModel + CastileTokenLogModel model.NhCastileTokenLogModel } func newDBModel(dbConn, gameConn sqlx.SqlConn, configModel model.NhSystemConfigModel) *dbModel { @@ -82,5 +85,8 @@ func newDBModel(dbConn, gameConn sqlx.SqlConn, configModel model.NhSystemConfigM MergeServerModel: model.NewMsMergeServersModel(gameConn), GameServerModel: model.NewMsGameServerModel(gameConn), ServersModel: model.NewMgServersModel(gameConn), + + CastileTokenModel: model.NewNhCastileTokenModel(dbConn), + CastileTokenLogModel: model.NewNhCastileTokenLogModel(dbConn), } } diff --git a/internal/types/types.go b/internal/types/types.go index 2f2f8e9..6b61e08 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -245,24 +245,20 @@ type TransferCastileToGameListReq struct { } type TransferCastileToGameListResp struct { - Total int `json:"total"` // 总数 - List []TransferToGameLog `json:"list"` // 列表 + Total int `json:"total"` // 总数 + List []TransferCastileToGameResp `json:"list"` // 列表 } type TransferCastileToGameReq struct { - RoleID int64 `json:"role_id"` // 角色id - Amount int64 `json:"amount"` // 数量 + RoleID int64 `json:"role_id"` // 角色id + Amount int64 `json:"amount,range=[1:]"` // 数量 } type TransferCastileToGameResp struct { - TransferToGameLog -} - -type TransferToGameLog struct { - Id int `json:"id"` // id - RoleID int64 `json:"role_id"` // 角色id - Amount int64 `json:"amount"` // 数量 - Status string `json:"elf_name"` // 状态 + Id int `json:"id"` // id + RoleID int64 `json:"role_id"` // 角色id + Amount int64 `json:"amount"` // 数量 + Status int64 `json:"status"` // 状态 } type UnStakeNftReq struct {