game/common/model/tableOperation.go

265 lines
6.6 KiB
Go
Raw Normal View History

2025-05-31 23:34:58 +08:00
package model
import (
"context"
"fmt"
"game/common/proto/pb"
"game/common/serialization"
2025-06-02 01:07:35 +08:00
"game/common/utils"
2025-05-31 23:34:58 +08:00
"github.com/fox/fox/log"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
2025-06-15 00:00:24 +08:00
"reflect"
"strconv"
2025-05-31 23:34:58 +08:00
"time"
)
const (
2025-06-02 01:07:35 +08:00
TableExpire = 7 * 24 * time.Hour // 七天后过期
2025-05-31 23:34:58 +08:00
)
type iTable interface {
2025-06-06 00:09:10 +08:00
GetId() int64
2025-06-04 01:56:38 +08:00
TableName() string
2025-05-31 23:34:58 +08:00
}
/*
T:Table如果不想操作redis则将rds设置为nil
*/
type TableOp[T iTable] struct {
db *gorm.DB
rds *redis.Client
}
2025-06-02 01:07:35 +08:00
func NewTableOp[T iTable](db *gorm.DB, rds *redis.Client) *TableOp[T] {
2025-05-31 23:34:58 +08:00
return &TableOp[T]{db: db, rds: rds}
}
func (s *TableOp[T]) tableName() string {
2025-06-14 16:40:15 +08:00
var result utils.TValue[T]
return result.V.TableName()
2025-05-31 23:34:58 +08:00
}
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) redisKey(id int64) string {
2025-05-31 23:34:58 +08:00
return fmt.Sprintf("%s:%d", s.tableName(), id)
}
2025-06-15 00:00:24 +08:00
// 查找并返回结构体
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) findByRedis(id int64) *T {
2025-06-15 00:00:24 +08:00
maps := s.findByRedisMaps(id)
if len(maps) == 0 {
return nil
}
us, err := serialization.MapToStruct[T](maps)
if err != nil {
log.ErrorF("serialization map to struct err: %v", err)
return nil
}
//log.DebugF("findByRedis redis-key:%v result:%v", s.redisKey(id), us)
return us
}
// 查找并返回map
func (s *TableOp[T]) findByRedisMaps(id int64) map[string]string {
2025-05-31 23:34:58 +08:00
if s.rds == nil {
return nil
}
maps, err := s.rds.HGetAll(context.Background(), s.redisKey(id)).Result()
if err != nil {
log.ErrorF("redis-key:%v HGetAll err: %v", s.redisKey(id), err)
return nil
}
if len(maps) == 0 {
return nil
}
2025-06-15 00:00:24 +08:00
return maps
2025-05-31 23:34:58 +08:00
}
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) writeRedis(id int64, t *T) {
2025-05-31 23:34:58 +08:00
if s.rds == nil {
return
}
maps := serialization.StructToMap(t)
if len(maps) == 0 {
log.ErrorF("table struct is empty:%v", s.tableName())
}
s.updateRedis(id, maps)
}
2025-06-14 16:40:15 +08:00
// 查看在redis中是否存在
func (s *TableOp[T]) existRedis(t *T) bool {
if s.rds == nil {
return false
}
// 查看redis中是否存在该键
exist, _ := s.rds.Exists(context.Background(), s.redisKey((*t).GetId())).Result()
return exist == 1
}
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) updateRedis(id int64, maps map[string]any) {
2025-05-31 23:34:58 +08:00
if s.rds == nil {
return
}
if err := s.rds.HMSet(context.Background(), s.redisKey(id), maps).Err(); err != nil {
log.ErrorF("redis-key:%v HMSet err: %v", s.redisKey(id), err)
}
2025-06-02 01:07:35 +08:00
_ = s.rds.Expire(context.Background(), s.redisKey(id), TableExpire).Err()
2025-05-31 23:34:58 +08:00
}
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) deleteRedis(id int64) {
2025-05-31 23:34:58 +08:00
if s.rds == nil {
return
}
_ = s.rds.Del(context.Background(), s.redisKey(id)).Err()
}
func (s *TableOp[T]) Create(t *T) (*T, pb.ErrCode) {
if err := s.db.Create(t).Error; err != nil {
log.ErrorF("create table:%v err:%v", s.tableName(), err)
return nil, pb.ErrCode_SystemErr
}
2025-06-02 01:07:35 +08:00
s.writeRedis((*t).GetId(), t)
2025-05-31 23:34:58 +08:00
return t, pb.ErrCode_OK
}
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) Find(id int64) (*T, pb.ErrCode) {
2025-05-31 23:34:58 +08:00
// 先从redis中查询redis中没有则从mysql中查询
if table := s.findByRedis(id); table != nil {
return table, pb.ErrCode_OK
}
2025-06-14 16:40:15 +08:00
var result utils.TValue[T]
err := s.db.Where("id = ?", id).First(&result.V).Error
2025-05-31 23:34:58 +08:00
if err != nil {
2025-06-04 01:56:38 +08:00
log.DebugF("find table:%v id:%v err:%v", s.tableName(), id, err)
2025-05-31 23:34:58 +08:00
return nil, pb.ErrCode_SystemErr
}
2025-06-14 16:40:15 +08:00
return &result.V, pb.ErrCode_OK
2025-05-31 23:34:58 +08:00
}
2025-06-02 01:07:35 +08:00
// 根据条件查询只在mysql中查询无法在redis中查询
func (s *TableOp[T]) FindCondition(condition map[string]any) (*T, error) {
2025-06-14 16:40:15 +08:00
var result utils.TValue[T]
err := s.db.Where(condition).First(&result.V).Error
2025-06-02 01:07:35 +08:00
if err != nil {
log.ErrorF("find table:%v condition:%v err:%v", s.tableName(), utils.JsonMarshal(condition), err)
return nil, err
}
// 查看redis中是否存在该键不存在则写入数据
2025-06-14 16:40:15 +08:00
if !s.existRedis(&result.V) {
s.writeRedis(result.V.GetId(), &result.V)
2025-06-02 01:07:35 +08:00
}
2025-06-14 16:40:15 +08:00
return &result.V, nil
2025-06-02 01:07:35 +08:00
}
2025-05-31 23:34:58 +08:00
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) Update(id int64, updates map[string]any) (*T, pb.ErrCode) {
2025-06-14 16:40:15 +08:00
var result utils.TValue[T]
err := s.db.Model(&result.V).Where("id = ?", id).Updates(updates).Error
2025-05-31 23:34:58 +08:00
if err != nil {
log.ErrorF("update table:%v id:%v err:%v", s.tableName(), id, err)
return nil, pb.ErrCode_SystemErr
}
s.updateRedis(id, updates)
2025-06-14 16:40:15 +08:00
return &result.V, pb.ErrCode_OK
}
2025-06-15 00:00:24 +08:00
// 辅助函数将map的keys转为字符串slice
func keysToStringSlice(m map[string]int64) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
// 获取资源
func (s *TableOp[T]) GetInt(id int64, resName []string) (map[string]int64, pb.ErrCode) {
mapFields := s.findByRedisMaps(id)
// 查询更新后的值到map
updatedValues := make(map[string]int64)
if len(mapFields) != 0 {
for _, name := range resName {
v, _ := strconv.ParseInt(mapFields[name], 10, 64)
updatedValues[name] = v
}
return updatedValues, pb.ErrCode_OK
}
// redis中没有值从表中加载并写入redis
mapAny := make(map[string]any)
err := s.db.Model(new(T)).
Where("id = ?", id).
Take(&mapAny). // 扫描到map
Error
if err != nil {
log.ErrorF("query updated values table:%v id:%v err:%v", s.tableName(), id, err)
return nil, pb.ErrCode_SystemErr
}
s.updateRedis(id, mapAny)
// 查询更新后的值到map
updatedValues = make(map[string]int64)
for _, name := range resName {
if val, ok := mapAny[name]; ok {
var v64 int64
switch v := val.(type) {
case int64:
v64 = v
case int, int32, uint, uint32, uint64:
v64 = reflect.ValueOf(v).Int()
case float32, float64:
v64 = int64(reflect.ValueOf(v).Float())
default:
// 处理无法转换的情况
}
updatedValues[name] = v64
}
}
return updatedValues, pb.ErrCode_OK
}
// 增加或减少资源
func (s *TableOp[T]) AddInt(id int64, res map[string]int64) (map[string]int64, pb.ErrCode) {
2025-06-14 16:40:15 +08:00
addRes := map[string]any{}
for k, v := range res {
addRes[k] = gorm.Expr(fmt.Sprintf("%v + ?", k), v)
}
var result utils.TValue[T]
err := s.db.Model(&result.V).Where("id = ?", id).Updates(addRes).Error
if err != nil {
log.ErrorF("add table:%v id:%v err:%v", s.tableName(), id, err)
return nil, pb.ErrCode_SystemErr
}
2025-06-15 00:00:24 +08:00
// 查询更新后的值到map
updatedValues := make(map[string]int64)
err = s.db.Model(new(T)).
Select(keysToStringSlice(res)). // 只选择需要返回的字段
Where("id = ?", id).
Take(&updatedValues). // 扫描到map
Error
if err != nil {
log.ErrorF("query updated values table:%v id:%v err:%v", s.tableName(), id, err)
return nil, pb.ErrCode_SystemErr
}
mapAny := make(map[string]any)
for k, v := range updatedValues {
mapAny[k] = v
}
s.updateRedis(id, mapAny)
return updatedValues, pb.ErrCode_OK
2025-05-31 23:34:58 +08:00
}
2025-06-06 00:09:10 +08:00
func (s *TableOp[T]) Delete(id int64) (*T, pb.ErrCode) {
2025-06-14 16:40:15 +08:00
var result utils.TValue[T]
err := s.db.Delete(&result.V, id).Error
2025-05-31 23:34:58 +08:00
if err != nil {
log.ErrorF("delete table:%v err:%v", s.tableName(), err)
return nil, pb.ErrCode_SystemErr
}
s.deleteRedis(id)
2025-06-14 16:40:15 +08:00
return &result.V, pb.ErrCode_OK
2025-05-31 23:34:58 +08:00
}