feat(cache): 添加复合键缓存操作支持

添加了基于 uint32+string 组合键的缓存操作方法,包括
GetByCompoundKey、SetByCompoundKey、DelByCompoundKey 和
ContainsByCompoundKey 方法,用于处理用户ID和会话ID的组合缓存场景

fix(vscode): 添加 cSpell 配置支持 struc 词汇

refactor(session): 移除过时的会话管理方法

移除了基于单一字符串键的会话管理方法,因为已迁移到使用
复合键的缓存操作方式
```
This commit is contained in:
昔念
2026-01-19 18:51:56 +08:00
parent 08ebf849eb
commit 026689f3ed
120 changed files with 1428 additions and 629 deletions

View File

@@ -0,0 +1,60 @@
package service
import (
"blazing/cool"
"blazing/modules/player/model"
"context"
"github.com/gogf/gf/v2/frame/g"
)
type BargeService struct {
BaseService
}
func NewBargeService(id uint32) *BargeService {
return &BargeService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewPetBargeListInfo()},
},
}
}
func (s *BargeService) Update(petid uint32, isskill bool) {
if cool.Config.ServerInfo.IsVip != 0 {
cool.Logger.Info(context.TODO(), "测试服不消耗物品玩家数据", s.userid)
return
}
if t, _ := s.PModel(s.Model).Where("pet_id", petid).Count(); t != 0 {
if isskill {
s.PModel(s.Model).Where("pet_id", petid).Increment("killed_count", 1)
} else {
s.PModel(s.Model).Where("pet_id", petid).Increment("catched_count", 1)
}
} else {
r := g.Map{
"player_id": s.userid,
"pet_id": petid,
}
if isskill {
r["killed_count"] = 1
} else {
r["catched_count"] = 1
}
s.PModel(s.Model).Data(r).Insert()
}
}
func (s *BargeService) Get(start, end uint32) []model.PetBargeListInfo {
var Barges []model.PetBargeListInfo
s.PModel(s.Model).WhereBetween("pet_id", start, end).Scan(&Barges)
return Barges
}

View File

@@ -0,0 +1,42 @@
package service
import (
"blazing/cool"
"blazing/modules/player/model"
"github.com/gogf/gf/v2/frame/g"
)
type CdkService struct {
BaseService
}
func (s *CdkService) CanGet(id uint32) bool {
m1, _ := s.TestModel(s.Model).Where("code_id", id).Exist()
return !m1
}
func (s *CdkService) Log(id uint32) {
m := s.TestModel(s.Model)
data := g.Map{
"player_id": s.userid,
"code_id": id,
"is_vip": cool.Config.ServerInfo.IsVip,
}
m.Data(data).Insert()
}
func NewCdkService(id uint32) *CdkService {
return &CdkService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewCdkLog()},
},
}
}

View File

@@ -0,0 +1,60 @@
package service
import (
"blazing/cool"
"blazing/modules/player/model"
"context"
"strings"
"github.com/gogf/gf/v2/util/gconv"
)
type DoneService struct {
BaseService
}
func (s *DoneService) Exec(data model.EnumMilestone, id []uint32, fn func(*model.MilestoneEX) bool) {
if cool.Config.ServerInfo.IsVip != 0 {
cool.Logger.Info(context.TODO(), "测试服不消耗物品玩家数据", s.userid)
return
}
arss := strings.Join(gconv.Strings(id), "-")
m := s.PModel(s.Model).Where("done_type", data).Where("args", arss)
var tt *model.MilestoneEX
m.Scan(&tt)
if tt == nil {
tt = &model.MilestoneEX{
Milestone: model.Milestone{
DoneType: data,
Args: strings.Join(gconv.Strings(id), "-"),
//Count: 1,
},
}
}
tt.Args = id
ook := fn(tt)
if !ook { //不需要保存
return
}
tt.PlayerID = uint64(s.userid)
tt.Milestone.Args = strings.Join(gconv.Strings(id), "-")
_, err := m.Save(tt)
if err != nil {
panic(err)
}
}
func NewDoneService(id uint32) *DoneService {
return &DoneService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewMilestone(), UniqueKey: map[string]string{
"player_id": "角色名称不能重复",
}},
},
}
}

View File

@@ -0,0 +1,212 @@
package service
import (
"blazing/common/data/share"
"blazing/cool"
"blazing/modules/config/service"
"blazing/modules/player/model"
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"strings"
"time"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/google/uuid"
csmap "github.com/mhmtszr/concurrent-swiss-map"
)
// 是否注册,如果注册过,那么就会产生用户player信息
func (s *InfoService) IsReg() bool {
m := s.PModel(s.Model)
record, err := m.One()
if err != nil {
return false
}
if record != nil {
return true
}
return false
}
// 实现注册,id+昵称+颜色
func (s *InfoService) Reg(nick string, color uint32) {
if s.IsReg() {
return
}
t := model.NewPlayer()
t.PlayerID = uint64(s.userid)
//设置用户信息
t.Data = model.NewPlayerInfo()
t.Data.Nick = nick
t.Data.UserID = s.userid
t.Data.Color = color
t.Data.RegisterTime = uint32(time.Now().Unix()) //写入注册时间
_, err := cool.DBM(s.Model).Data(t).FieldsEx("id").Insert()
if err != nil {
glog.Error(context.Background(), err)
return
}
//go s.InitTask()
}
func (s *InfoService) Person(userid uint32) *model.PlayerInfo {
m := cool.DBM(s.Model).Where("player_id", userid)
var tt model.PlayerEX
err := m.Scan(&tt)
if err != nil {
return nil
}
ret := tt.Data
return &ret
}
func (s *InfoService) GetCache() *model.PlayerInfo {
ret, _ := cool.CacheManager.Get(context.TODO(), fmt.Sprintf("player: %d", s.userid))
if ret == nil {
return nil
}
var rets *model.PlayerInfo
ret.Struct(&rets)
return rets
}
func (s *InfoService) SetLogin() *model.PlayerInfo {
m := cool.DBM(s.Model).Where("player_id", s.userid)
var tt *model.PlayerEX
m.Scan(&tt)
if tt == nil {
return nil
}
tt.Data.AllPetNumber = uint32(NewPetService(s.userid).PetCount(0))
if !IsToday(tt.LastResetTime) { //判断是否是今天
//每天login时候检查重置时间然后把电池任务挖矿重置
//挖矿需要单独存,因为防止多开挖矿
tt.LastResetTime = gtime.Now()
//每天login时候检查重置时间然后把电池任务挖矿重置
//挖矿需要单独存,因为防止多开挖矿
tt.Data.TimeToday = 0 //重置电池
for _, v := range service.NewTaskService().GetDaily() {
tt.Data.SetTask(int(v.TaskId), model.Unaccepted)
}
for i := 0; i < 50; i++ { //每日任务区段
tt.Data.DailyResArr[i] = 0 //重置每日任务
}
//defer t.Service.Talk_Reset()
_, err := m.Save(tt)
if err != nil {
panic(err)
}
}
ret := tt.Data
return &ret
}
var User = csmap.New[string, uint32](
// set the number of map shards. the default value is 32.
csmap.WithShardCount[string, uint32](32),
// set the total capacity, every shard map has total capacity/shard count capacity. the default value is 0.
// csmap.WithSize[string, int](1000),
)
// 生成session
// GetSessionId 生成并返回会话ID、UUID字符串及可能的错误
// 会话ID由accountID(4字节) + UUID(16字节) + 随机数(4字节)组成,最终编码为十六进制字符串
func (s *InfoService) Gensession() string {
uuidV7, _ := uuid.NewV7()
// 移除UUID中的连字符便于后续处理
uuidStr := strings.ReplaceAll(uuidV7.String(), "-", "")
// 解码UUID字符串为字节数组32位十六进制字符串对应16字节
uuidBytes, _ := hex.DecodeString(uuidStr)
// 将accountID转换为4字节大端序字节数组
accountBytes := make([]byte, 4)
binary.BigEndian.PutUint32(accountBytes, uint32(s.userid))
// 预分配缓冲区总长度4+16+4=24字节减少内存分配
sessionBytes := make([]byte, 0, 24)
sessionBytes = append(sessionBytes, accountBytes...)
sessionBytes = append(sessionBytes, uuidBytes...)
//sessionBytes = append(sessionBytes, grand.B(4)...)
// 编码为十六进制字符串作为最终会话ID
sessionID := hex.EncodeToString(sessionBytes)
User.Store(string(uuidStr), uint32(s.userid))
//share.ShareManager.SaveSession(string(uuidStr), uint32(s.userid))
return sessionID
}
func (s *InfoService) Kick(id uint32) error {
cool.Logger.Info(context.TODO(), "服务器收到踢人")
useid1, err := share.ShareManager.GetUserOnline(id)
if err != nil {
return err
}
cl, ok := cool.GetClient(useid1)
if ok {
err := cl.KickPerson(id) //实现指定服务器踢人
if err != nil {
return err
}
}
return nil
}
func (s *InfoService) Save(data model.PlayerInfo) {
m := s.PModel(s.Model)
var tt model.PlayerEX
m.Scan(&tt)
tt.Data = data
_, err := m.Save(tt)
if err != nil {
panic(err)
}
}
type InfoService struct {
BaseService
}
func NewInfoService(id uint32) *InfoService {
return &InfoService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewPlayer(), UniqueKey: map[string]string{
"player_id": "角色名称不能重复",
}, PageQueryOp: &cool.QueryOp{
FieldEQ: []string{"player_id"},
}},
},
}
}

View File

@@ -0,0 +1,85 @@
package service
import (
"blazing/cool"
"blazing/modules/player/model"
"context"
"github.com/gogf/gf/v2/frame/g"
)
func (s *ItemService) Get(min, max uint32) []model.Item {
m := s.TestModel(s.Model).Where(g.Map{
"item_id <=": max,
"item_id >=": min,
})
var ttt []model.Item
m.Scan(&ttt)
return ttt
}
func (s *ItemService) UPDATE(id uint32, count int) {
if cool.Config.ServerInfo.IsVip != 0 && count < 0 {
cool.Logger.Info(context.TODO(), "测试服不消耗物品玩家数据", s.userid)
return
}
m := s.TestModel(s.Model)
if t, _ := m.Where("item_id", id).Exist(); t {
_, err := s.TestModel(s.Model).Where("item_id", id).Increment("item_cnt", count)
if err != nil {
panic(err)
}
} else {
m := s.TestModel(s.Model)
data := g.Map{
"player_id": s.userid,
"item_id": id,
"item_cnt": count,
"is_vip": cool.Config.ServerInfo.IsVip,
}
m.Data(data).Insert()
}
}
func (s *ItemService) CheakItem(id uint32) uint32 {
var ttt model.Item
m := s.TestModel(s.Model)
m.Where("item_id", id).Scan(&ttt)
return ttt.ItemCnt
}
// /添加进来的物品一定是保证存在的
type ItemService struct {
BaseService
}
func NewItemService(id uint32) *ItemService {
return &ItemService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewPlayerBag(), UniqueKey: map[string]string{
"player_id": "角色名称不能重复",
}, PageQueryOp: &cool.QueryOp{
KeyWordField: []string{"player_id"},
Where: func(ctx context.Context) [][]interface{} {
var (
//admin = cool.GetAdmin(ctx)
//userId = admin.UserId
)
return [][]interface{}{
// {"player_id", userId, true},
// {"free", 0, true},
}
},
}},
},
}
}

View File

@@ -0,0 +1,179 @@
package service
import (
"blazing/cool"
"blazing/modules/base/service"
"blazing/modules/player/model"
"context"
"fmt"
"github.com/gogf/gf/v2/util/gconv"
)
// 获取精灵信息 0是仓库,1是放生
func (s *PetService) PetInfo(flag int) []model.PetEX {
var tt []model.PetEX
err := s.TestModel(s.Model).Where("free", flag).Scan(&tt)
if err != nil {
return []model.PetEX{}
}
for i := 0; i < len(tt); i++ {
tt[i].Data.CatchTime = tt[i].CatchTime
}
return tt
}
func (s *PetService) PetCount(flag int) int {
ret, err := s.TestModel(s.Model).Where("player_id", s.userid).Where("free", flag).Count()
if err != nil {
return 0
}
return ret
}
func (s *PetService) UPdateFree(ctime uint32, free uint32) {
s.TestModel(s.Model).Where("player_id", s.userid).Where("catch_time", ctime).Data(
"free", free,
).Update()
}
func (s *PetService) UPdate(t model.PetInfo) {
m := s.TestModel(s.Model).Where("player_id", s.userid).Where("catch_time", t.CatchTime)
var tt model.PetEX
m.Scan(&tt)
if tt.CatchTime == 0 {
return
}
tt.Data = t
_, err := m.OnConflict("catch_time").Update(tt)
if err != nil {
panic(err)
}
}
func (s *PetService) PetInfo_One(cachetime uint32) *model.PetEX {
m := s.TestModel(s.Model).Where("player_id", s.userid).Where("catch_time", cachetime)
var tt *model.PetEX
m.Scan(&tt)
tt.Data.CatchTime = tt.CatchTime
return tt
}
func (s *PetService) PetInfo_One_ohter(userid, cachetime uint32) model.PetEX {
m := s.TestModel(s.Model).Where("player_id", userid).Where("catch_time", cachetime)
var tt model.PetEX
m.Scan(&tt)
tt.Data.CatchTime = tt.CatchTime
return tt
}
func (s *PetService) PetInfo_One_Unscoped(cachetime uint32) model.PetEX {
m := s.TestModel(s.Model).Where("player_id", s.userid).Where("catch_time", cachetime).Unscoped()
var tt model.PetEX
m.Scan(&tt)
tt.Data.CatchTime = tt.CatchTime
return tt
}
func (s *PetService) Pet_del(cachetime uint32) {
s.TestModel(s.Model).Where("player_id", s.userid).Where("catch_time", cachetime).Delete()
}
// 精灵真正添加后的捕捉时间才是真正的时间
func (s *PetService) PetAdd(y *model.PetInfo) {
if y == nil {
return
}
sql := fmt.Sprintf(`
UPDATE %s
SET max_ts = CASE
WHEN max_ts < EXTRACT(EPOCH FROM NOW())::INT THEN EXTRACT(EPOCH FROM NOW())::INT
ELSE max_ts + 1
END
WHERE id = ? AND deleted_at IS NULL
RETURNING max_ts;
`, service.NewBaseSysUserService().Model.TableName())
// 执行 Raw SQL 并扫描返回值
ret, err := cool.DBM(service.NewBaseSysUserService().Model).Raw(sql, s.userid).All()
//fmt.Println(ret, err)
y.CatchTime = ret.Array()[0].Uint32()
m1 := cool.DBM(s.Model).Where("player_id", s.userid)
var player model.PetEX
player.PlayerID = s.userid
player.Data = *y
player.CatchTime = y.CatchTime
player.Free = 0
player.IsVip = cool.Config.ServerInfo.IsVip
_, err = m1.Insert(player)
if err != nil {
panic(err)
}
}
func (s *PetService) ModifyBefore(ctx context.Context, method string, param map[string]interface{}) (err error) {
admin := cool.GetAdmin(ctx)
userId := admin.UserId
s.userid = uint32(userId)
if method == "Update" {
if gconv.Uint(param["free"]) != 0 {
err = fmt.Errorf("修改失败")
}
if userId != gconv.Uint(param["player_id"]) {
err = fmt.Errorf("修改失败")
}
}
return
}
type PetService struct {
BaseService
}
func NewPetService(userid uint32) *PetService {
return &PetService{
BaseService: BaseService{
userid: userid,
Service: &cool.Service{
Model: model.NewPet(),
PageQueryOp: &cool.QueryOp{
Where: func(ctx context.Context) [][]interface{} {
var (
admin = cool.GetAdmin(ctx)
userId = admin.UserId
)
if userId != 10001 {
return [][]interface{}{
{"player_id", userId, true},
{"free", 1, true},
}
} else {
return [][]interface{}{
{"player_id", userId, true},
{"free", 1, true},
}
}
},
},
},
},
}
}

View File

@@ -0,0 +1,105 @@
package service
import (
"blazing/cool"
"blazing/modules/player/model"
"context"
)
func (s *RoomService) Get(userid uint32) model.BaseHouseEx {
//todo待测试
var ttt model.BaseHouseEx
m := s.TestModel(s.Model)
m.Where("player_id", userid).Scan(&ttt)
return ttt
}
// func (s *RoomService) Add(id, count uint32) {
// //todo待测试
// var ttt model.BaseHouseEx
// m := s.TestModel(s.Model)
// m.Scan(&ttt)
// if ttt.OwnedItems == nil {
// ttt.OwnedItems = make(map[uint32]uint32)
// }
// t, ok := ttt.OwnedItems[id]
// if ok {
// ttt.OwnedItems[id] = t + count
// } else {
// ttt.OwnedItems[id] = count
// }
// ttt.PlayerID = uint64(s.userid)
// m.Save(ttt)
// }
func (s *RoomService) Set(id []model.FitmentShowInfo) {
//todo待测试
var ttt model.BaseHouseEx
m := s.TestModel(s.Model)
m.Scan(&ttt)
ttt.PlacedItems = id
ttt.UsedItems = make(map[uint32]uint32)
for _, ids := range id {
_, ok := ttt.UsedItems[ids.Id]
if !ok {
ttt.UsedItems[ids.Id] = 1
} else {
ttt.UsedItems[ids.Id] = ttt.UsedItems[ids.Id] + 1
}
}
ttt.PlayerID = uint64(s.userid)
m.Save(ttt)
}
func (s *RoomService) Show(cactime []uint32) {
//todo待测试
var ttt model.BaseHouseEx
m := s.TestModel(s.Model)
m.Scan(&ttt)
ttt.ShowPokemon = cactime
ttt.PlayerID = uint64(s.userid)
m.Save(ttt)
}
// /添加进来的物品一定是保证存在的
type RoomService struct {
BaseService
}
func NewRoomService(id uint32) *RoomService {
return &RoomService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewBaseHouse(), UniqueKey: map[string]string{
"player_id": "角色名称不能重复",
}, PageQueryOp: &cool.QueryOp{
KeyWordField: []string{"player_id"},
Where: func(ctx context.Context) [][]interface{} {
var (
//admin = cool.GetAdmin(ctx)
//userId = admin.UserId
)
return [][]interface{}{
// {"player_id", userId, true},
// {"free", 0, true},
}
},
}},
},
}
}

View File

@@ -0,0 +1,75 @@
package service
import (
"blazing/cool"
config "blazing/modules/config/service"
"blazing/modules/player/model"
"context"
)
type TalkService struct {
BaseService
}
func NewTalkService(id uint32) *TalkService {
return &TalkService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewTalk()},
},
}
}
//实现挖矿次数确认
func (s *TalkService) Cheak(mapid uint32, flag int) (int, bool) {
m1 := s.PModel(s.Model)
var talks *model.Talk
m1.Where("talk_id", flag).Scan(&talks)
if talks == nil {
talks = model.NewTalk()
talks.PlayerID = uint64(s.userid)
talks.TalkID = uint32(flag)
s.PModel(s.Model).Data(talks).FieldsEx("id").Insert()
return 0, true //如果表里没有记载数据,那么就可以直接挖矿
}
//因为这个是挖一次更新一次,而且是实时更新的,如果更新日期是今天,那么就可以确认不用再重置,否则就需要重置挖矿记录
if !IsToday(talks.UpdateTime) {
talks.Count = 0
m1.Save(talks)
return int(talks.Count), true
}
if uint32(mapid) != config.NewTalkConfigService().GetCache(flag).MapID {
return 0, false //没在地图
}
if talks.Count >= config.NewTalkConfigService().GetCache(flag).DailyCollectCount {
return 0, false
}
return int(talks.Count), true //int(config.MaxDailyCnt - talks.Count)
}
func (s *TalkService) Update(flag int) {
if cool.Config.ServerInfo.IsVip != 0 {
cool.Logger.Info(context.TODO(), "测试服不消耗物品玩家数据", s.userid)
return
}
m1 := s.PModel(s.Model)
var talks model.Talk
m1.Where("talk_id", flag).Scan(&talks)
//talks.PlayerID = uint64(s.userid)
//talks.TalkID = uint32(flag)
talks.Count += 1
m1.Save(talks)
}

View File

@@ -0,0 +1,58 @@
package service
import (
"blazing/cool"
"blazing/modules/player/model"
"time"
"github.com/gogf/gf/v2/os/gtime"
)
// 获取任务信息
func (s *TaskService) Exec(id uint32, t func(*model.TaskEX) bool) {
var gg model.TaskEX
m1 := s.PModel(s.Model).Where("task_id", id)
m1.Scan(&gg)
tre := t(&gg)
if !tre { //不需要更新
return
}
gg.PlayerID = uint64(s.userid)
gg.TaskID = id
_, err := m1.Save(gg)
if err != nil {
panic(err)
}
}
// IsToday 判断给定时间是否是今天
func IsToday(t1 *gtime.Time) bool {
if t1 == nil {
return false
}
t := t1.Time
// 获取当前时间
now := time.Now()
// 比较年、月、日是否相同
return t.Year() == now.Year() &&
t.Month() == now.Month() &&
t.Day() == now.Day()
}
type TaskService struct {
BaseService
}
func NewTaskService(id uint32) *TaskService {
return &TaskService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewTask()},
},
}
}

View File

@@ -0,0 +1,75 @@
package service
import (
"blazing/cool"
"blazing/modules/player/model"
"github.com/gogf/gf/v2/frame/g"
"github.com/samber/lo"
)
type TitleService struct {
BaseService
}
func (s *TitleService) Get() []uint32 {
m1 := s.TestModel(s.Model)
var talks *model.Title
m1.Scan(&talks)
if talks == nil {
return []uint32{}
}
return talks.AvailableTitle
}
func (s *TitleService) Can(id uint32) bool {
m1 := s.TestModel(s.Model)
var talks *model.Title
m1.Scan(&talks)
if talks == nil {
return false
}
_, ok := lo.Find(talks.AvailableTitle, func(item uint32) bool {
return item == id
})
return ok
}
func (s *TitleService) Give(id uint32) bool {
m1 := s.TestModel(s.Model)
var talks *model.Title
m1.Scan(&talks)
if talks == nil {
m := s.TestModel(s.Model)
data := g.Map{
"player_id": s.userid,
"available_title": []uint32{id},
"is_vip": cool.Config.ServerInfo.IsVip,
}
m.Data(data).Insert()
return true
}
talks.AvailableTitle = append(talks.AvailableTitle, id)
m1.Save()
return true
}
func NewTitleService(id uint32) *TitleService {
return &TitleService{
BaseService: BaseService{userid: id,
Service: &cool.Service{Model: model.NewPlayerTitle()},
},
}
}

View File

@@ -0,0 +1,61 @@
package service
import (
"blazing/cool"
"github.com/gogf/gf/v2/database/gdb"
)
type BaseService struct {
userid uint32
*cool.Service
}
type UserService struct {
Talk *TalkService //挖矿
Task *TaskService //任务
Info *InfoService //信息
Pet *PetService //精灵
Item *ItemService //物品
Done *DoneService //完成
Room *RoomService
Barge *BargeService
Title *TitleService
Cdk *CdkService
}
func NewUserService(id uint32) *UserService {
return &UserService{
Task: NewTaskService(id),
Info: NewInfoService(id),
Pet: NewPetService(id),
Item: NewItemService(id),
Talk: NewTalkService(id),
Done: NewDoneService(id),
Room: NewRoomService(id),
Barge: NewBargeService(id),
Title: NewTitleService(id),
Cdk: NewCdkService(id),
}
}
func (s *BaseService) TestModel(m cool.IModel) *gdb.Model {
m1 := cool.DBM(m)
if s.userid != 0 {
m1.Where("player_id", s.userid)
}
m1.Where("is_vip", cool.Config.ServerInfo.IsVip)
return m1
}
func (s *BaseService) PModel(m cool.IModel) *gdb.Model {
m1 := cool.DBM(m)
if s.userid != 0 {
m1.Where("player_id", s.userid)
}
return m1
}