game/common/model/tableOperation.go
2025-06-16 00:50:42 +08:00

265 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package model
import (
"context"
"fmt"
"game/common/proto/pb"
"game/common/serialization"
"game/common/utils"
"github.com/fox/fox/log"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
"reflect"
"strconv"
"time"
)
const (
TableExpire = 7 * 24 * time.Hour // 七天后过期
)
type iTable interface {
GetId() int64
TableName() string
}
/*
T:Table如果不想操作redis则将rds设置为nil
*/
type TableOp[T iTable] struct {
db *gorm.DB
rds *redis.Client
}
func NewTableOp[T iTable](db *gorm.DB, rds *redis.Client) *TableOp[T] {
return &TableOp[T]{db: db, rds: rds}
}
func (s *TableOp[T]) tableName() string {
var result utils.TValue[T]
return result.V.TableName()
}
func (s *TableOp[T]) redisKey(id int64) string {
return fmt.Sprintf("%s:%d", s.tableName(), id)
}
// 查找并返回结构体
func (s *TableOp[T]) findByRedis(id int64) *T {
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 {
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
}
return maps
}
func (s *TableOp[T]) writeRedis(id int64, t *T) {
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)
}
// 查看在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
}
func (s *TableOp[T]) updateRedis(id int64, maps map[string]any) {
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)
}
_ = s.rds.Expire(context.Background(), s.redisKey(id), TableExpire).Err()
}
func (s *TableOp[T]) deleteRedis(id int64) {
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
}
s.writeRedis((*t).GetId(), t)
return t, pb.ErrCode_OK
}
func (s *TableOp[T]) Find(id int64) (*T, pb.ErrCode) {
// 先从redis中查询redis中没有则从mysql中查询
if table := s.findByRedis(id); table != nil {
return table, pb.ErrCode_OK
}
var result utils.TValue[T]
err := s.db.Where("id = ?", id).First(&result.V).Error
if err != nil {
log.DebugF("find table:%v id:%v err:%v", s.tableName(), id, err)
return nil, pb.ErrCode_SystemErr
}
return &result.V, pb.ErrCode_OK
}
// 根据条件查询只在mysql中查询无法在redis中查询
func (s *TableOp[T]) FindCondition(condition map[string]any) (*T, pb.ErrCode) {
us := new(T)
err := s.db.Where(condition).First(us).Error
if err != nil {
log.ErrorF("find table:%v condition:%v err:%v", s.tableName(), utils.JsonMarshal(condition), err)
return nil, pb.ErrCode_SystemErr
}
// 查看redis中是否存在该键不存在则写入数据
if !s.existRedis(us) {
s.writeRedis((*us).GetId(), us)
}
return us, pb.ErrCode_OK
}
func (s *TableOp[T]) Update(id int64, updates map[string]any) (*T, pb.ErrCode) {
var result utils.TValue[T]
err := s.db.Model(&result.V).Where("id = ?", id).Updates(updates).Error
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)
return &result.V, pb.ErrCode_OK
}
// 辅助函数将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) {
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
}
// 查询更新后的值到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
}
func (s *TableOp[T]) Delete(id int64) (*T, pb.ErrCode) {
var result utils.TValue[T]
err := s.db.Delete(&result.V, id).Error
if err != nil {
log.ErrorF("delete table:%v err:%v", s.tableName(), err)
return nil, pb.ErrCode_SystemErr
}
s.deleteRedis(id)
return &result.V, pb.ErrCode_OK
}