初始化项目
This commit is contained in:
46
trunk/goutil/rank-util/model.go
Normal file
46
trunk/goutil/rank-util/model.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package rank_util
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Model
|
||||
// @description: 排行榜对象
|
||||
type Model struct {
|
||||
// rank 排行
|
||||
rank int
|
||||
|
||||
// key 唯一key
|
||||
key string
|
||||
|
||||
// obj 携带的对象
|
||||
obj interface{}
|
||||
}
|
||||
|
||||
// GetObj
|
||||
// @description: 获取对象附加信息
|
||||
// parameter:
|
||||
// @receiver m:
|
||||
// return:
|
||||
// @interface{}:
|
||||
func (m *Model) GetObj() interface{} {
|
||||
return m.obj
|
||||
}
|
||||
|
||||
// GetRank
|
||||
// @description: 获取对象排行
|
||||
// parameter:
|
||||
// @receiver m:
|
||||
// return:
|
||||
// @int:
|
||||
func (m *Model) GetRank() int {
|
||||
return m.rank
|
||||
}
|
||||
|
||||
// String
|
||||
// @description: 字符串
|
||||
// parameter:
|
||||
// @receiver m:
|
||||
// return:
|
||||
// @string:
|
||||
func (m *Model) String() string {
|
||||
return fmt.Sprintf("rank:%v, Key:%s, Obj:{%s}", m.rank, m.key, m.obj)
|
||||
}
|
||||
148
trunk/goutil/rank-util/rank_test.go
Normal file
148
trunk/goutil/rank-util/rank_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package rank_util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"goutil/mathUtil"
|
||||
"math"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Rank(t *testing.T) {
|
||||
r := NewRankUtil(20, compar)
|
||||
|
||||
dc := make(map[string]*rmodel)
|
||||
for i := 1; i < 1000; i++ {
|
||||
tk := strconv.Itoa(i)
|
||||
m := &rmodel{k: tk, Fap: 0}
|
||||
dc[m.k] = m
|
||||
}
|
||||
|
||||
goCount := 10
|
||||
srand := mathUtil.GetRand()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(goCount)
|
||||
for i := 0; i < goCount; i++ {
|
||||
go func() {
|
||||
for i := 1; i < 1000; i++ {
|
||||
tk := strconv.Itoa(i)
|
||||
newf := srand.GetRandRangeInt(1, 1000)
|
||||
|
||||
m, _ := dc[tk]
|
||||
isUp := m.Fap < newf
|
||||
m.Fap = newf
|
||||
r.Refresh(tk, m, isUp)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
items := r.GetAll()
|
||||
if len(items) != r.maxCount {
|
||||
t.Error("排行榜长度不为", r.maxCount)
|
||||
}
|
||||
|
||||
//测试删除
|
||||
r.Delete(r.GetItemForRank(1).key)
|
||||
items = r.GetAll()
|
||||
if len(items) != r.maxCount-1 {
|
||||
t.Error("删除后的排行榜长度不为", r.maxCount-1)
|
||||
}
|
||||
|
||||
//校验标准: 1.key不可以重复 2.fap越来越小
|
||||
beforeFap := math.MaxInt
|
||||
tempKeyDict := make(map[string]bool)
|
||||
isPrintAll := false
|
||||
for _, item := range items {
|
||||
m := item.obj.(*rmodel)
|
||||
if m.k != item.key {
|
||||
t.Errorf("错误:item.key!=m.k item.key:%s m.k:%s", item.key, m.k)
|
||||
isPrintAll = true
|
||||
}
|
||||
|
||||
// 校验1
|
||||
if _, exist := tempKeyDict[item.key]; exist {
|
||||
t.Errorf("错误:item.key重复 item.key:%s m.k:%s", item.key, m.k)
|
||||
isPrintAll = true
|
||||
} else {
|
||||
tempKeyDict[item.key] = false
|
||||
}
|
||||
|
||||
// 校验2
|
||||
if m.Fap > beforeFap {
|
||||
t.Errorf("错误:m.Fap大于前面的排行 beforeFap:%v curFap:%v", beforeFap, m.Fap)
|
||||
isPrintAll = true
|
||||
}
|
||||
}
|
||||
|
||||
if isPrintAll {
|
||||
for _, item := range items {
|
||||
m := item.obj.(*rmodel)
|
||||
t.Log(m)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_Rank2(t *testing.T) {
|
||||
r := NewRankUtil(5, compar)
|
||||
r.Refresh("1", &rmodel{k: "1", Fap: 1}, true)
|
||||
r.Refresh("2", &rmodel{k: "2", Fap: 2}, true)
|
||||
r.Refresh("3", &rmodel{k: "3", Fap: 3}, true)
|
||||
r.Refresh("4", &rmodel{k: "4", Fap: 4}, true)
|
||||
r.Refresh("5", &rmodel{k: "5", Fap: 5}, true)
|
||||
|
||||
//把1挤出去
|
||||
ischange, dm := r.Refresh("6", &rmodel{k: "6", Fap: 6}, true)
|
||||
if ischange == false {
|
||||
t.Errorf("把1挤出去错误,排行榜未变动")
|
||||
return
|
||||
}
|
||||
if dm.key != "1" {
|
||||
t.Errorf("把1挤出去错误,挤出的不是1")
|
||||
return
|
||||
}
|
||||
t.Log("被挤出的对象信息-> ", dm)
|
||||
|
||||
//打印所有
|
||||
items := r.GetAll()
|
||||
for _, item := range items {
|
||||
t.Log(item)
|
||||
}
|
||||
}
|
||||
|
||||
type rmodel struct {
|
||||
k string
|
||||
Fap int
|
||||
}
|
||||
|
||||
func (m *rmodel) String() string {
|
||||
return fmt.Sprintf("m.k:%s fap:%v", m.k, m.Fap)
|
||||
}
|
||||
|
||||
// compar
|
||||
// @description: 判断对象大小,返回含义 -1:a<b 0:a=b 1:a>b
|
||||
// parameter:
|
||||
//
|
||||
// @a:对象a
|
||||
// @b:对象b
|
||||
//
|
||||
// return:
|
||||
//
|
||||
// @int:
|
||||
func compar(a, b interface{}) int {
|
||||
af := a.(*rmodel).Fap
|
||||
bf := b.(*rmodel).Fap
|
||||
if af > bf {
|
||||
return 1
|
||||
} else if af == bf {
|
||||
return 0
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
54
trunk/goutil/rank-util/readme.md
Normal file
54
trunk/goutil/rank-util/readme.md
Normal file
@@ -0,0 +1,54 @@
|
||||
提供游戏内的实时排行榜功能,建议最大长度设定<=200
|
||||
使用方式:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
rank_util "goutil/rank-util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 构造对象
|
||||
r := rank_util.NewRankUtil(20, compar)
|
||||
|
||||
// 刷新排行榜
|
||||
m := &rmodel{k: "byrontest", Fap: 110}
|
||||
ifChangeRank, dm := r.Refresh(m.k, m, true)
|
||||
|
||||
// 获取全部排行榜
|
||||
tempList := r.GetAll()
|
||||
|
||||
// 删除某个key
|
||||
isok := r.Delete("byrontest")
|
||||
}
|
||||
|
||||
// compar
|
||||
// @description: 判断对象大小,返回含义 -1:a<b 0:a=b 1:a>b
|
||||
// parameter:
|
||||
// @a:对象a
|
||||
// @b:对象b
|
||||
// return:
|
||||
// @int:
|
||||
func compar(a, b interface{}) int {
|
||||
af := a.(*rmodel).Fap
|
||||
bf := b.(*rmodel).Fap
|
||||
if af > bf {
|
||||
return 1
|
||||
} else if af == bf {
|
||||
return 0
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
type rmodel struct {
|
||||
k string
|
||||
Fap int
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
343
trunk/goutil/rank-util/util.go
Normal file
343
trunk/goutil/rank-util/util.go
Normal file
@@ -0,0 +1,343 @@
|
||||
package rank_util
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RankUtil
|
||||
// @description:排行工具类
|
||||
type RankUtil struct {
|
||||
// maxCount 排行榜最大数量
|
||||
maxCount int
|
||||
|
||||
// compar 对比函数 返回含义 -1:a<b 0:a=b 1:a>b
|
||||
compar func(a, b interface{}) int
|
||||
|
||||
// dataList 排行数据
|
||||
dataList []*Model
|
||||
|
||||
// dataDict 数据字典
|
||||
dataDict map[string]*Model
|
||||
|
||||
// dataLock 数据锁
|
||||
dataLock sync.RWMutex
|
||||
}
|
||||
|
||||
// NewRankUtil
|
||||
// @description: 构造排行对象
|
||||
// parameter:
|
||||
// @mc:最大容量
|
||||
// @comp:元素比对方法
|
||||
// return:
|
||||
func NewRankUtil(mc int, comp func(a, b interface{}) int) *RankUtil {
|
||||
result := &RankUtil{
|
||||
maxCount: mc,
|
||||
compar: comp,
|
||||
dataList: make([]*Model, 0, mc),
|
||||
dataDict: make(map[string]*Model, mc),
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetMaxCount
|
||||
// @description: 获取排行榜最大数量
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// return:
|
||||
// @int:
|
||||
func (r *RankUtil) GetMaxCount() int {
|
||||
return r.maxCount
|
||||
}
|
||||
|
||||
// GetCount
|
||||
// @description: 获取排行榜当前数量
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// return:
|
||||
// @int:
|
||||
func (r *RankUtil) GetCount() int {
|
||||
r.dataLock.RLock()
|
||||
defer r.dataLock.RUnlock()
|
||||
|
||||
return len(r.dataList)
|
||||
}
|
||||
|
||||
// IsFull
|
||||
// @description: 判断排行榜是否已经满了
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// return:
|
||||
// @bool:
|
||||
func (r *RankUtil) IsFull() bool {
|
||||
r.dataLock.RLock()
|
||||
defer r.dataLock.RUnlock()
|
||||
|
||||
return len(r.dataList) == r.maxCount
|
||||
}
|
||||
|
||||
// GetAll
|
||||
// @description: 获取排行榜内容
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// return:
|
||||
// @[]*Model:
|
||||
func (r *RankUtil) GetAll() []*Model {
|
||||
r.dataLock.RLock()
|
||||
defer r.dataLock.RUnlock()
|
||||
|
||||
result := make([]*Model, 0, len(r.dataList))
|
||||
for _, item := range r.dataList {
|
||||
if item == nil || item.obj == nil {
|
||||
break
|
||||
}
|
||||
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetRankListBySkip
|
||||
// @description: 获取排行榜内容
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @skip:跳过的数量
|
||||
// @count:跳过后需要返回的数量
|
||||
// return:
|
||||
// @[]*Model:
|
||||
func (r *RankUtil) GetRankListBySkip(skip, count int) []*Model {
|
||||
r.dataLock.RLock()
|
||||
defer r.dataLock.RUnlock()
|
||||
|
||||
result := make([]*Model, 0, count)
|
||||
cn := len(r.dataList)
|
||||
if skip >= cn {
|
||||
return result
|
||||
}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
item := r.dataList[skip+i]
|
||||
if item == nil || item.obj == nil {
|
||||
break
|
||||
}
|
||||
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetItemForK
|
||||
// @description: 根据key获取对象
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @k:key
|
||||
// return:
|
||||
// @*Model:
|
||||
func (r *RankUtil) GetItemForK(k string) *Model {
|
||||
r.dataLock.RLock()
|
||||
defer r.dataLock.RUnlock()
|
||||
|
||||
item, _ := r.dataDict[k]
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
// GetItemForRank
|
||||
// @description: 根据排名获取对应对象
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @rank:排名
|
||||
// return:
|
||||
// @*Model:
|
||||
func (r *RankUtil) GetItemForRank(rank int) *Model {
|
||||
r.dataLock.RLock()
|
||||
defer r.dataLock.RUnlock()
|
||||
|
||||
if len(r.dataList) < rank {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.dataList[rank-1]
|
||||
}
|
||||
|
||||
// Refresh
|
||||
// @description: 刷新排行榜
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @k:key
|
||||
// @o:排行持有的对象,业务层不应该持有该对象。否则业务层更改内容,会导致排行榜内容被改变
|
||||
// @isup:变动是否升高
|
||||
// return:
|
||||
// @changeRank:排行是否变动
|
||||
// @dm:如果有对象,因为排行变动导致掉出排行榜,则返回该对象,否则返回nil
|
||||
func (r *RankUtil) Refresh(k string, o interface{}, isup bool) (changeRank bool, dm *Model) {
|
||||
r.dataLock.Lock()
|
||||
defer r.dataLock.Unlock()
|
||||
|
||||
changeRank = false
|
||||
curItem, exists := r.dataDict[k]
|
||||
if isup {
|
||||
if !exists {
|
||||
newRank := len(r.dataList) + 1
|
||||
curItem = &Model{rank: newRank, key: k, obj: o}
|
||||
// 排行榜未满,必定入榜,则先加入排行榜
|
||||
if newRank <= r.maxCount {
|
||||
r.dataList = append(r.dataList, curItem)
|
||||
r.dataDict[curItem.key] = curItem
|
||||
changeRank = true
|
||||
_, dm = r.upRank(curItem)
|
||||
} else {
|
||||
changeRank, dm = r.upRank(curItem)
|
||||
}
|
||||
} else {
|
||||
curItem.obj = o
|
||||
changeRank, dm = r.upRank(curItem)
|
||||
}
|
||||
} else {
|
||||
// 如果在排行榜内,从之前往下比较
|
||||
if exists {
|
||||
curItem.obj = o
|
||||
changeRank = r.downRank(curItem)
|
||||
} else {
|
||||
// 如果不在排行榜内,直接结束
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Delete
|
||||
// @description: 删除k指定的对象,很耗性能,不建议高频使用
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @k:key
|
||||
// return:
|
||||
// @bool:true:代表key存在排行榜中,并且删除成功 false:其他情况
|
||||
func (r *RankUtil) Delete(k string) bool {
|
||||
r.dataLock.Lock()
|
||||
defer r.dataLock.Unlock()
|
||||
|
||||
m, exists := r.dataDict[k]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
//从当前位置向后移动,到最后的时候,删除
|
||||
for {
|
||||
if m.rank >= len(r.dataList) {
|
||||
break
|
||||
}
|
||||
|
||||
am := r.dataList[m.rank]
|
||||
r.exchange(m, am)
|
||||
m = am
|
||||
}
|
||||
|
||||
//最后一个应该是要删除的对象
|
||||
r.dataList = append(r.dataList[:m.rank-1:r.maxCount], r.dataList[m.rank:]...)
|
||||
delete(r.dataDict, k)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// upRank
|
||||
// @description: 排行升高
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @curItem:当前对象
|
||||
// return:
|
||||
// @changeRank:排行是否变动
|
||||
// @dm:因排行变动被移除排行的对象
|
||||
func (r *RankUtil) upRank(curItem *Model) (changeRank bool, dm *Model) {
|
||||
changeRank = false
|
||||
for {
|
||||
// rank=1,排在第一位,下标为0,故这里需要-2为上一位的下标
|
||||
beforeIndex := curItem.rank - 2
|
||||
if beforeIndex < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
beforeItem := r.dataList[beforeIndex]
|
||||
if r.compar(curItem.obj, beforeItem.obj) <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// 交换操作
|
||||
tdm := r.exchange(curItem, beforeItem)
|
||||
if tdm != nil {
|
||||
dm = tdm
|
||||
}
|
||||
|
||||
curItem = beforeItem
|
||||
changeRank = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// downRank
|
||||
// @description: 排行降低
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @curItem:当前对象
|
||||
// return:
|
||||
// @bool:
|
||||
func (r *RankUtil) downRank(curItem *Model) bool {
|
||||
changeRank := false
|
||||
|
||||
for {
|
||||
// rank=1,排在第一位,下标为0,需要对比rank=2的,下标为1,故这里直接取rank即可
|
||||
afterIndex := curItem.rank
|
||||
if afterIndex >= len(r.dataList) {
|
||||
break
|
||||
}
|
||||
|
||||
afterItem := r.dataList[afterIndex]
|
||||
if r.compar(afterItem.obj, curItem.obj) <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// 交换操作
|
||||
r.exchange(curItem, afterItem)
|
||||
|
||||
curItem = afterItem
|
||||
changeRank = true
|
||||
}
|
||||
|
||||
return changeRank
|
||||
}
|
||||
|
||||
// exchange
|
||||
// @description: 排行对象交换
|
||||
// parameter:
|
||||
// @receiver r:
|
||||
// @a:对象a
|
||||
// @b:对象b
|
||||
// return:
|
||||
// @delRank:因交换被移除排行榜的对象
|
||||
func (r *RankUtil) exchange(a, b *Model) (dm *Model) {
|
||||
tempObj := a.obj
|
||||
tempKey := a.key
|
||||
|
||||
a.obj = b.obj
|
||||
a.key = b.key
|
||||
b.obj = tempObj
|
||||
b.key = tempKey
|
||||
|
||||
if a.rank > r.maxCount {
|
||||
delete(r.dataDict, a.key)
|
||||
dm = a
|
||||
} else {
|
||||
r.dataDict[a.key] = a
|
||||
}
|
||||
if b.rank > r.maxCount {
|
||||
delete(r.dataDict, b.key)
|
||||
dm = b
|
||||
} else {
|
||||
r.dataDict[b.key] = b
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user