From c12c49477c45d5d7c7f35d5a8a6e3880e084ef6c Mon Sep 17 00:00:00 2001 From: ayflying Date: Tue, 2 Sep 2025 16:51:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8C=81=E4=B9=85=E5=8C=96?= =?UTF-8?q?=E5=9B=A0=E4=B8=BA=E6=B4=BB=E5=8A=A8=E5=A4=AA=E5=A4=9A=E9=80=A0?= =?UTF-8?q?=E6=88=90=E7=9A=84=E9=80=9F=E5=BA=A6=E5=A4=AA=E6=85=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/boot/boot.go | 2 +- internal/logic/gameAct/gameAct.go | 172 +++++++++++++++++++++++++++++- service/game_act.go | 39 +++++++ 3 files changed, 211 insertions(+), 2 deletions(-) diff --git a/internal/boot/boot.go b/internal/boot/boot.go index db64015..811c5fd 100644 --- a/internal/boot/boot.go +++ b/internal/boot/boot.go @@ -22,7 +22,7 @@ func Boot() (err error) { service.SystemCron().AddCronV2(v1.CronType_HOUR, func(context.Context) error { go func() { err = service.GameKv().SavesV1() - err = service.GameAct().Saves() + err = service.GameAct().SavesV2() if err != nil { g.Log().Error(gctx.New(), err) } diff --git a/internal/logic/gameAct/gameAct.go b/internal/logic/gameAct/gameAct.go index 479b56b..c575489 100644 --- a/internal/logic/gameAct/gameAct.go +++ b/internal/logic/gameAct/gameAct.go @@ -110,6 +110,12 @@ func (s *sGameAct) Set(uid int64, actId int, data interface{}) (err error) { return } +// Saves 保存游戏活动数据 +// +// @Description: 保存游戏活动数据 +// @receiver s *sGameAct: 游戏活动服务结构体指针 +// @return err error: 返回错误信息 +// Deprecated: 该方法已被弃用,建议使用SavesV2方法 func (s *sGameAct) Saves() (err error) { var ctx = gctx.New() g.Log().Debug(ctx, "开始执行游戏act数据保存了") @@ -130,12 +136,19 @@ func (s *sGameAct) Saves() (err error) { return } +// Save 保存游戏活动数据 +// +// @Description: 保存游戏活动数据 +// @receiver s *sGameAct: 游戏活动服务结构体指针 +// @param ctx context.Context: 上下文对象 +// @param actId int: 活动ID +// @return err error: 返回错误信息 +// deprecated: 该方法已被弃用,建议使用SaveV2方法 func (s *sGameAct) Save(ctx context.Context, actId int) (err error) { cacheKey := fmt.Sprintf("act:%v:*", actId) var add = make([]*entity.GameAct, 0) var update = make([]*entity.GameAct, 0) - //循环获取缓存数据 err = tools.Redis.RedisScanV2(cacheKey, func(keys []string) (err error) { //判断是否超时 @@ -268,6 +281,163 @@ func (s *sGameAct) Save(ctx context.Context, actId int) (err error) { return } +// SavesV2 保存游戏活动数据 +// +// @Description: 保存游戏活动数据 +// @receiver s *sGameAct: 游戏活动服务结构体指针 +// @return err error: 返回错误信息 +func (s *sGameAct) SavesV2() (err error) { + var ctx = gctx.New() + g.Log().Debug(ctx, "开始执行游戏act数据保存了") + //如果没有执行过,设置时间戳 + // 最大允许执行时间 + RunTimeMax = gtime.Now().Add(time.Minute * 30) + + //cacheKey := fmt.Sprintf("act:%v:*", actId) + var add = make([]*entity.GameAct, 0) + var update = make([]*entity.GameAct, 0) + + //循环获取缓存数据 + err = tools.Redis.RedisScanV2("act:*", func(keys []string) (err error) { + for _, key := range keys { + //格式化数据 + err = s.SaveV2(ctx, key, add, update) + //持久化数据 + err = s.Cache2Sql(ctx, add, update) + } + return err + }) + + return +} + +// SaveV2 保存游戏活动数据 +// +// @Description: 保存游戏活动数据 +// @receiver s *sGameAct: 游戏活动服务结构体指针 +// @param ctx context.Context: 上下文对象 +// @param cacheKey string: 缓存键 +// @param add []*entity.GameAct: 添加数据 +// @param update []*entity.GameAct: 更新数据 +// @return err error: 返回错误信息 +func (s *sGameAct) SaveV2(ctx context.Context, cacheKey string, add, update []*entity.GameAct) (err error) { + + result := strings.Split(cacheKey, ":") + actId := gconv.Int(result[1]) + if actId == 0 { + return + } + var uid int64 + uid = gconv.Int64(result[2]) + if uid == 0 { + //跳过为空的用户缓存 + return + } + + //获取缓存数据 + cacheGet, _ := g.Redis().Get(ctx, cacheKey) + + if cacheGet.IsEmpty() { + //空数据也不保存 + return + } + + //如果有活跃,跳过持久化 + if getBool, _ := pkg.Cache("redis"). + Contains(ctx, fmt.Sprintf("act:update:%d", uid)); getBool { + return + } + + //获取数据库数据 + var data *entity.GameAct + // 从数据库中查询活动信息 + err = g.Model(Name).Where(do.GameAct{ + Uid: uid, + ActId: actId, + }).Fields("uid,act_id").Scan(&data) + if err != nil { + g.Log().Errorf(ctx, "当前数据错误: %v", cacheKey) + return + } + //如果没有数据,添加 + actionData := cacheGet.String() + if data == nil { + add = append(add, &entity.GameAct{ + ActId: actId, + Uid: uid, + Action: actionData, + }) + } else { + //覆盖数据 + data.ActId = actId + data.Uid = uid + data.Action = actionData + update = append(update, data) + } + + return +} + +// Cache2Sql 缓存持久化到数据库 +// @Description: 缓存持久化到数据库 +// @receiver s *sGameAct: 游戏活动服务结构体指针 +// @param ctx context.Context: 上下文对象 +// @param add []*entity.GameAct: 添加数据 +// @param update []*entity.GameAct: 更新数据 +// @return err error: 返回错误信息 +func (s *sGameAct) Cache2Sql(ctx context.Context, add, update []*entity.GameAct) (err error) { + //批量写入数据库 + updateCount := 0 + if len(update) > 100 { + for _, v := range update { + v.UpdatedAt = gtime.Now() + updateRes, err2 := g.Model(Name).Where(do.GameAct{ + Uid: v.Uid, + ActId: v.ActId, + }).Data(v).Update() + if err2 != nil { + g.Log().Error(ctx, err2) + continue + } + + if row, _ := updateRes.RowsAffected(); row == 0 { + g.Log().Error(ctx, "本次更新为0,更新数据失败: %v", v) + continue + } + + //删除缓存 + go s.DelCacheKey(ctx, v.ActId, v.Uid) + + //updateCount++ + update = make([]*entity.GameAct, 0) + } + + g.Log().Debugf(ctx, "act当前更新数据库: %v 条", updateCount) + update = make([]*entity.GameAct, 0) + } + + var addCount int64 + if len(add) > 100 { + for _, v := range add { + addRes, err2 := g.Model(Name).Data(v).Insert() + if err2 != nil { + g.Log().Error(ctx, err2) + continue + } + if row, _ := addRes.RowsAffected(); row == 0 { + g.Log().Error(ctx, "本次新增为0,新增数据失败: %v", v) + continue + } + addCount++ + //删除缓存 + go s.DelCacheKey(ctx, v.ActId, v.Uid) + } + g.Log().Debugf(ctx, "act当前写入数据库: %v 条", addCount) + add = make([]*entity.GameAct, 0) + } + return +} + // 删除缓存key func (s *sGameAct) DelCacheKey(ctx context.Context, aid int, uid int64) { cacheKey := fmt.Sprintf("act:%v:%v", aid, uid) diff --git a/service/game_act.go b/service/game_act.go index 1544d47..3a4ebcf 100644 --- a/service/game_act.go +++ b/service/game_act.go @@ -8,6 +8,7 @@ package service import ( "context" + "github.com/ayflying/utility_go/internal/model/entity" "github.com/gogf/gf/v2/frame/g" ) @@ -31,8 +32,46 @@ type ( // @param data interface{}: 要存储的活动信息数据。 // @return err error: 返回错误信息,如果操作成功,则返回nil。 Set(uid int64, actId int, data interface{}) (err error) + // Saves 保存游戏活动数据 + // + // @Description: 保存游戏活动数据 + // @receiver s *sGameAct: 游戏活动服务结构体指针 + // @return err error: 返回错误信息 + // Deprecated: 该方法已被弃用,建议使用SavesV2方法 Saves() (err error) + // Save 保存游戏活动数据 + // + // @Description: 保存游戏活动数据 + // @receiver s *sGameAct: 游戏活动服务结构体指针 + // @param ctx context.Context: 上下文对象 + // @param actId int: 活动ID + // @return err error: 返回错误信息 + // deprecated: 该方法已被弃用,建议使用SaveV2方法 Save(ctx context.Context, actId int) (err error) + // SavesV2 保存游戏活动数据 + // + // @Description: 保存游戏活动数据 + // @receiver s *sGameAct: 游戏活动服务结构体指针 + // @return err error: 返回错误信息 + SavesV2() (err error) + // SaveV2 保存游戏活动数据 + // + // @Description: 保存游戏活动数据 + // @receiver s *sGameAct: 游戏活动服务结构体指针 + // @param ctx context.Context: 上下文对象 + // @param cacheKey string: 缓存键 + // @param add []*entity.GameAct: 添加数据 + // @param update []*entity.GameAct: 更新数据 + // @return err error: 返回错误信息 + SaveV2(ctx context.Context, cacheKey string, add []*entity.GameAct, update []*entity.GameAct) (err error) + // Cache2Sql 缓存持久化到数据库 + // @Description: 缓存持久化到数据库 + // @receiver s *sGameAct: 游戏活动服务结构体指针 + // @param ctx context.Context: 上下文对象 + // @param add []*entity.GameAct: 添加数据 + // @param update []*entity.GameAct: 更新数据 + // @return err error: 返回错误信息 + Cache2Sql(ctx context.Context, add []*entity.GameAct, update []*entity.GameAct) (err error) // 删除缓存key DelCacheKey(ctx context.Context, aid int, uid int64) // 清空GetRedDot缓存