Compare commits

...

3 Commits

Author SHA1 Message Date
ayflying
0cfaa799f5 时间传入不修改时区 2025-02-25 16:38:35 +08:00
ayflying
36a34891db 时间函数使用utc时间计算,预防切换夏令时bug 2025-02-25 15:19:42 +08:00
ayflying
4862dcd1d8 跟新结构体切片 2025-02-11 14:26:06 +08:00
8 changed files with 254 additions and 35 deletions

View File

@@ -1,6 +1,7 @@
package aycache package aycache
import ( import (
"github.com/ayflying/utility_go/aycache/drive"
"github.com/gogf/gf/v2/os/gcache" "github.com/gogf/gf/v2/os/gcache"
) )
@@ -24,6 +25,10 @@ func New(_name ...string) gcache.Adapter {
cacheAdapterObj = NewAdapterMemory() cacheAdapterObj = NewAdapterMemory()
case "redis": case "redis":
cacheAdapterObj = NewAdapterRedis() cacheAdapterObj = NewAdapterRedis()
case "file":
cacheAdapterObj = NewAdapterFile("runtime/cache")
case "es":
cacheAdapterObj = drive.NewAdapterElasticsearch([]string{"http://127.0.0.1:9200"})
} }
//var client = gcache.New() //var client = gcache.New()

View File

@@ -0,0 +1,119 @@
package drive
import (
"context"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/os/gcache"
"time"
)
type AdapterElasticsearch struct {
//FilePath string
Addresses []string
}
func (a AdapterElasticsearch) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) SetMap(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (ok bool, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) SetIfNotExistFunc(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (ok bool, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) SetIfNotExistFuncLock(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (ok bool, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Get(ctx context.Context, key interface{}) (*gvar.Var, error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (result *gvar.Var, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) GetOrSetFunc(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (result *gvar.Var, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) GetOrSetFuncLock(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (result *gvar.Var, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Contains(ctx context.Context, key interface{}) (bool, error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Size(ctx context.Context) (size int, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Data(ctx context.Context) (data map[interface{}]interface{}, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Keys(ctx context.Context) (keys []interface{}, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Values(ctx context.Context) (values []interface{}, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Update(ctx context.Context, key interface{}, value interface{}) (oldValue *gvar.Var, exist bool, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Remove(ctx context.Context, keys ...interface{}) (lastValue *gvar.Var, err error) {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Clear(ctx context.Context) error {
//TODO implement me
panic("implement me")
}
func (a AdapterElasticsearch) Close(ctx context.Context) error {
//TODO implement me
panic("implement me")
}
func NewAdapterElasticsearch(addresses []string) gcache.Adapter {
return &AdapterElasticsearch{
Addresses: addresses,
}
}

View File

@@ -4,6 +4,10 @@ import (
"context" "context"
"github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/os/gcache" "github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/util/gconv"
"path"
"strings"
"time" "time"
) )
@@ -12,8 +16,20 @@ type AdapterFile struct {
} }
func (a AdapterFile) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error { func (a AdapterFile) Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error {
//TODO implement me //defer a.handleLruKey(ctx, key)
panic("implement me") //expireTime := a.getInternalExpire(duration)
//a.data.Set(key, memoryDataItem{
// a: value,
// a: expireTime,
//})
//c.eventList.PushBack(&adapterMemoryEvent{
// k: key,
// e: expireTime,
//})
arr := strings.Split(":", gconv.String(key))
fileName := path.Join(arr...)
return gfile.PutBytes(fileName, gconv.Bytes(value))
} }
func (a AdapterFile) SetMap(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error { func (a AdapterFile) SetMap(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) error {

View File

@@ -93,7 +93,7 @@ func (s *Excel) itemsFormat(list []interface{}, Items []string) []interface{} {
for k3, v3 := range v2.(g.Map) { for k3, v3 := range v2.(g.Map) {
if gstr.InArray(Items, k3) { if gstr.InArray(Items, k3) {
if _, ok := v3.(string); ok { if _, ok := v3.(string); ok {
list[k2].(g.Map)[k3] = s.Spilt2Item(v3.(string)) list[k2].(g.Map)[k3] = Spilt2Item(v3.(string))
} else { } else {
g.Log().Errorf(gctx.New(), "当前类型断言失败:%v,list=%v", v3, v2) g.Log().Errorf(gctx.New(), "当前类型断言失败:%v,list=%v", v3, v2)
} }
@@ -108,7 +108,7 @@ func (s *Excel) itemsMapFormat(list []interface{}, Items []string) []interface{}
for k3, v3 := range v2.(g.Map) { for k3, v3 := range v2.(g.Map) {
if gstr.InArray(Items, k3) { if gstr.InArray(Items, k3) {
if _, ok := v3.(string); ok { if _, ok := v3.(string); ok {
get := s.Spilt2Item(v3.(string)) get := Spilt2Item(v3.(string))
list[k2].(g.Map)[k3] = s.Items2Map(get) list[k2].(g.Map)[k3] = s.Items2Map(get)
} else { } else {
g.Log().Errorf(gctx.New(), "当前类型断言失败:%v,list=%v", v3, v2) g.Log().Errorf(gctx.New(), "当前类型断言失败:%v,list=%v", v3, v2)

View File

@@ -26,7 +26,7 @@ func Excel2Slice(filePath string, _sheet ...string) [][]string {
} }
// 字符串转道具类型 // 字符串转道具类型
func (s *Excel) Spilt2Item(str string) (result [][]int64) { func Spilt2Item(str string) (result [][]int64) {
var shadiao = []string{","} var shadiao = []string{","}
for _, v := range shadiao { for _, v := range shadiao {
str = strings.ReplaceAll(str, v, "|") str = strings.ReplaceAll(str, v, "|")
@@ -39,20 +39,6 @@ func (s *Excel) Spilt2Item(str string) (result [][]int64) {
parts = []string{str} parts = []string{str}
} }
//var parts []string
//for _, v := range parts1 {
// parts = append(parts, strings.Split(v, ",")...) // 分割字符串
//}
//for _, v := range parts1 {
// parts2 := strings.Split(v, ",") // 分割字符串
// if parts2 == nil {
// parts = append(parts, v)
// } else {
// parts = append(parts, parts2...)
// }
//}
for i := 0; i < len(parts)-1; i += 2 { for i := 0; i < len(parts)-1; i += 2 {
num1, _ := strconv.ParseInt(parts[i], 10, 64) num1, _ := strconv.ParseInt(parts[i], 10, 64)
num2, _ := strconv.ParseInt(parts[i+1], 10, 64) num2, _ := strconv.ParseInt(parts[i+1], 10, 64)

View File

@@ -36,8 +36,18 @@ func (s *Mod) Load() {
} }
// CreateF64CountRank 创建一个排行榜实例 name: [name:赛季] // CreateF64CountRank 创建一个排行榜实例
// 参数:
//
// name: 排行榜的名称,通常代表一个赛季
//
// 返回值:
//
// *F64CountRank: 返回一个指向新创建的F64CountRank实例的指针
func (s *Mod) CreateF64CountRank(name string) *F64CountRank { func (s *Mod) CreateF64CountRank(name string) *F64CountRank {
// 初始化F64CountRank实例的name和updateTs字段
// name字段用于标识排行榜的名称格式为"rank:<name>:score"
// updateTs字段用于标识排行榜的更新时间格式为"rank:<name>:updateTs"
return &F64CountRank{ return &F64CountRank{
name: fmt.Sprintf("rank:%s:score", name), name: fmt.Sprintf("rank:%s:score", name),
updateTs: fmt.Sprintf("rank:%s:updateTs", name), updateTs: fmt.Sprintf("rank:%s:updateTs", name),
@@ -95,51 +105,71 @@ func (r *F64CountRank) GetCount() {
} }
} }
// 删除当前排行榜 // Delete 删除当前排行榜
// 该方法通过删除Redis中与排行榜相关的键来清除排行榜信息
func (r *F64CountRank) Delete() { func (r *F64CountRank) Delete() {
// 删除排行榜数据键
_, err := g.Redis().Del(ctx, r.name) _, err := g.Redis().Del(ctx, r.name)
if err != nil { if err != nil {
// 如果删除失败,记录错误日志
g.Log().Error(ctx, "排行榜删除失败:%v", err) g.Log().Error(ctx, "排行榜删除失败:%v", err)
} }
// 删除排行榜更新时间键
_, err = g.Redis().Del(ctx, r.updateTs) _, err = g.Redis().Del(ctx, r.updateTs)
if err != nil { if err != nil {
// 如果删除失败,记录错误日志
g.Log().Error(ctx, "排行榜删除失败:%v", err) g.Log().Error(ctx, "排行榜删除失败:%v", err)
} }
} }
// DelScore 删除当前分数 // DelScore 删除当前分数
// //
// @Description: // 该方法从更新时间有序集合和排名有序集合中移除指定的id。
// @receiver r // 这通常用于从排行榜中删除一个条目,同时确保其在更新时间集合中的对应记录也被清除。
// @param id //
// @return err // @Description: 从更新时间和排名集合中移除指定id
// @receiver r 接收者为F64CountRank类型的实例
// @param id 要从集合中移除的条目的ID
// @return err 可能发生的错误如果操作成功err为nil
func (r *F64CountRank) DelScore(id int64) (err error) { func (r *F64CountRank) DelScore(id int64) (err error) {
// 从更新时间集合中移除id
_, err = g.Redis().ZRem(ctx, r.updateTs, id) _, err = g.Redis().ZRem(ctx, r.updateTs, id)
// 从排名集合中移除id
_, err = g.Redis().ZRem(ctx, r.name, id) _, err = g.Redis().ZRem(ctx, r.name, id)
return return
} }
// DelScore 删除当前分数 // DelByRank 根据排名范围删除元素。
// // 该方法使用了Redis的有序集合数据结构通过ZRange和ZRemRangeByRank命令来实现。
// @Description: // 参数start和stop定义了要删除的排名范围从start到stop包括start和stop
// @receiver r // 返回可能的错误。
// @param id
// @return err
func (r *F64CountRank) DelByRank(start int64, stop int64) (err error) { func (r *F64CountRank) DelByRank(start int64, stop int64) (err error) {
// 初始化一个空的int64切片用于存储指定排名范围内的元素。
var members []int64 var members []int64
// 使用Redis的ZRange命令获取指定排名范围内的元素。
// 选项Rev设置为true表示按照分数从高到低的顺序返回元素。
get, err := g.Redis().ZRange(ctx, r.name, start, stop, get, err := g.Redis().ZRange(ctx, r.name, start, stop,
gredis.ZRangeOption{ gredis.ZRangeOption{
Rev: true, Rev: true,
}) })
// 使用Scan方法将获取到的元素扫描到members切片中。
err = get.Scan(&members) err = get.Scan(&members)
// 如果扫描过程中出现错误,直接返回错误。
if err != nil { if err != nil {
return return
} }
// 遍历members切片对于每个元素使用ZRem命令从更新时间集合中删除对应的成员。
for _, member := range members { for _, member := range members {
_, err = g.Redis().ZRem(ctx, r.updateTs, member) _, err = g.Redis().ZRem(ctx, r.updateTs, member)
// 忽略ZRem操作的错误因为即使元素不存在ZRem也不会返回错误。
} }
// 使用ZRemRangeByRank命令从有序集合中删除指定排名范围内的元素。
_, err = g.Redis().ZRemRangeByRank(ctx, r.name, start, stop) _, err = g.Redis().ZRemRangeByRank(ctx, r.name, start, stop)
// 返回可能的错误。
return return
} }
@@ -278,44 +308,85 @@ func (r *F64CountRank) UpdateScore(id int64, score int64) (err error) {
//} //}
// GetRankInfosNotTs 获取0~count跳记录 不根据更新时间来 // GetRankInfosNotTs 获取0~count跳记录 不根据更新时间来
// 该方法使用ZRange命令从Redis中获取指定范围的排名信息不考虑更新时间
// 参数:
//
// offset - 获取记录的起始偏移量
// count - 获取记录的数量
//
// 返回值:
//
// list - 排名信息列表
// err - 错误信息,如果执行过程中遇到错误
func (r *F64CountRank) GetRankInfosNotTs(offset, count int) (list []*Data, err error) { func (r *F64CountRank) GetRankInfosNotTs(offset, count int) (list []*Data, err error) {
// 初始化存储成员ID的切片
var members []int64 var members []int64
// 使用Redis的ZRange命令获取指定范围的成员ID
// 参数Rev设为true以从高分到低分获取成员
get, err := g.Redis().ZRange(ctx, r.name, int64(offset), int64(count), get, err := g.Redis().ZRange(ctx, r.name, int64(offset), int64(count),
gredis.ZRangeOption{ gredis.ZRangeOption{
Rev: true, Rev: true,
}) //.ScanSlice(&members) }) //.ScanSlice(&members)
// 将获取的结果扫描到members切片中
err = get.Scan(&members) err = get.Scan(&members)
// 如果发生错误,记录日志并返回
if err != nil { if err != nil {
//logs.Withf("redis err:%v", err) //logs.Withf("redis err:%v", err)
return return
} }
// 根据获取的成员ID数量初始化排名信息列表
list = make([]*Data, len(members)) list = make([]*Data, len(members))
for i := range members { for i := range members {
// 获取当前成员ID
id := members[i] id := members[i]
//id := pgk.InterfaceToNumber[uint64](members[i]) // 使用成员ID获取排名信息不考虑更新时间
list[i] = r.GetIdRankNotTs(id) list[i] = r.GetIdRankNotTs(id)
} }
// 返回排名信息列表和可能的错误
return return
} }
// 获取指定id的当前排名 // GetIdRankNotTs 获取指定id的当前排名
// 该方法从Redis的有序集合中查询指定id的分数和排名信息不考虑时间戳
// 参数:
//
// id - 需要查询排名的id
//
// 返回值:
//
// rankInfo - 包含id的分数和排名信息的指针如果没有找到则返回nil
func (r *F64CountRank) GetIdRankNotTs(id int64) (rankInfo *Data) { func (r *F64CountRank) GetIdRankNotTs(id int64) (rankInfo *Data) {
// 初始化rankInfo结构体设置id其他字段将通过查询填充
rankInfo = &Data{Id: id} rankInfo = &Data{Id: id}
// 查询有序集合中指定id的分数
score, err := g.Redis().ZScore(ctx, r.name, id) score, err := g.Redis().ZScore(ctx, r.name, id)
if err != nil { if err != nil {
// 如果发生错误直接返回rankInfo为初始化状态Id已设置其他字段为零值
return return
} }
// 将分数转换为int64类型并更新rankInfo
rankInfo.Score = int64(score) rankInfo.Score = int64(score)
// 如果分数为0直接返回表示该id的分数为0没有进一步查询排名的必要
if score == 0 { if score == 0 {
return return
} }
// 查询有序集合中指定id的排名
rank, err := g.Redis().ZRevRank(ctx, r.name, id) rank, err := g.Redis().ZRevRank(ctx, r.name, id)
if err != nil { if err != nil {
// 如果发生错误直接返回rankInfo中仅分数有效排名信息未更新
return return
} }
// 更新rankInfo中的排名信息排名从0开始所以需要加1以符合人类的计数习惯
rankInfo.Rank = int32(rank) + 1 rankInfo.Rank = int32(rank) + 1
// 返回包含完整排名信息的rankInfo指针
return rankInfo return rankInfo
} }

View File

@@ -60,14 +60,13 @@ func (m *timeMod) GetDayPass(startTime time.Time, t ...time.Time) int {
day := startTime.Day() day := startTime.Day()
// 构建一天的开始时间 // 构建一天的开始时间
startTime = time.Date(year, month, day, 0, 0, 0, 0, startTime.Location()) startTime = time.Date(year, month, day, 0, 0, 0, 0, startTime.Location())
//如果为空,使用当前时间 //如果为空,使用当前时间
endTime := time.Now() endTime := time.Now()
if len(t) > 0 { if len(t) > 0 {
endTime = t[0] endTime = t[0]
} }
//计算过去了多少天 //计算过去了多少天
dayPass := int(endTime.Sub(startTime).Hours() / 24) dayPass := int(endTime.UTC().Sub(startTime.UTC()).Hours() / 24)
return dayPass return dayPass
} }

View File

@@ -55,9 +55,32 @@ func (m *tools) GetDay(t1 *gtime.Time, t2 *gtime.Time) int {
return int(t1.Sub(t2).Hours() / 24) return int(t1.Sub(t2).Hours() / 24)
} }
//// 字符串转道具类型
//func (m *tools) Spilt2Item(str string) (result [][]int64) {
// parts := strings.Split(str, "|") // 分割字符串
//
// for i := 0; i < len(parts)-1; i += 2 {
// num1, _ := strconv.ParseInt(parts[i], 10, 64)
// num2, _ := strconv.ParseInt(parts[i+1], 10, 64)
//
// pair := []int64{num1, num2}
// result = append(result, pair)
// }
// return
//}
// 字符串转道具类型 // 字符串转道具类型
func (m *tools) Spilt2Item(str string) (result [][]int64) { func (m *tools) Spilt2Item(str string) (result [][]int64) {
var shadiao = []string{","}
for _, v := range shadiao {
str = strings.ReplaceAll(str, v, "|")
}
//var parts []string
parts := strings.Split(str, "|") // 分割字符串 parts := strings.Split(str, "|") // 分割字符串
if parts == nil {
parts = []string{str}
}
for i := 0; i < len(parts)-1; i += 2 { for i := 0; i < len(parts)-1; i += 2 {
num1, _ := strconv.ParseInt(parts[i], 10, 64) num1, _ := strconv.ParseInt(parts[i], 10, 64)