refactor(common/utils): 重构concurrent_swiss_map使用官方sync.Map实现 - 替换原有的第三方并发map实现,改为基于标准库sync.Map的封装 - 保持完全的API兼容性,原有配置方法变为无实际作用的占位符 - 优化Range方法实现,移除goroutine/channel开销,避免潜在的死锁风险 - 移除依赖的外部库和
This commit is contained in:
@@ -1,284 +1,191 @@
|
|||||||
package csmap
|
package csmap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"blazing/cool"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/mhmtszr/concurrent-swiss-map/maphash"
|
|
||||||
|
|
||||||
"github.com/mhmtszr/concurrent-swiss-map/swiss"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CsMap 基于官方 sync.Map 重构,完全兼容原有接口
|
||||||
type CsMap[K comparable, V any] struct {
|
type CsMap[K comparable, V any] struct {
|
||||||
hasher func(key K) uint64
|
inner sync.Map // 核心替换为官方 sync.Map
|
||||||
shards []shard[K, V]
|
|
||||||
shardCount uint64
|
|
||||||
size uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HashShardPair[K comparable, V any] struct {
|
// 以下配置方法保留(兼容原有调用方式,但内部无实际作用)
|
||||||
shard shard[K, V]
|
|
||||||
hash uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type shard[K comparable, V any] struct {
|
|
||||||
items *swiss.Map[K, V]
|
|
||||||
*sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// OptFunc is a type that is used in New function for passing options.
|
|
||||||
type OptFunc[K comparable, V any] func(o *CsMap[K, V])
|
type OptFunc[K comparable, V any] func(o *CsMap[K, V])
|
||||||
|
|
||||||
// New function creates *CsMap[K, V].
|
// New 创建基于 sync.Map 的并发安全 Map,兼容原有配置参数(参数无实际作用)
|
||||||
func New[K comparable, V any](options ...OptFunc[K, V]) *CsMap[K, V] {
|
func New[K comparable, V any](options ...OptFunc[K, V]) *CsMap[K, V] {
|
||||||
m := CsMap[K, V]{
|
m := &CsMap[K, V]{}
|
||||||
hasher: maphash.NewHasher[K]().Hash,
|
// 遍历配置项(兼容原有代码,无实际逻辑)
|
||||||
shardCount: uint64(runtime.NumCPU()),
|
|
||||||
}
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
option(&m)
|
option(m)
|
||||||
|
}
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
m.shards = make([]shard[K, V], m.shardCount)
|
// 保留原有配置方法(空实现,保证接口兼容)
|
||||||
|
|
||||||
for i := 0; i < int(m.shardCount); i++ {
|
|
||||||
m.shards[i] = shard[K, V]{items: swiss.NewMap[K, V](uint32((m.size / m.shardCount) + 1)), RWMutex: &sync.RWMutex{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &m
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Create creates *CsMap.
|
|
||||||
// //
|
|
||||||
// // Deprecated: New function should be used instead.
|
|
||||||
// func Create[K comparable, V any](options ...func(options *CsMap[K, V])) *CsMap[K, V] {
|
|
||||||
// m := CsMap[K, V]{
|
|
||||||
// hasher: maphash.NewHasher[K]().Hash,
|
|
||||||
// shardCount: 32,
|
|
||||||
// }
|
|
||||||
// for _, option := range options {
|
|
||||||
// option(&m)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// m.shards = make([]shard[K, V], m.shardCount)
|
|
||||||
|
|
||||||
// for i := 0; i < int(m.shardCount); i++ {
|
|
||||||
// m.shards[i] = shard[K, V]{items: swiss.NewMap[K, V](uint32((m.size / m.shardCount) + 1)), RWMutex: &sync.RWMutex{}}
|
|
||||||
// }
|
|
||||||
// return &m
|
|
||||||
// }
|
|
||||||
|
|
||||||
func WithShardCount[K comparable, V any](count uint64) func(csMap *CsMap[K, V]) {
|
func WithShardCount[K comparable, V any](count uint64) func(csMap *CsMap[K, V]) {
|
||||||
return func(csMap *CsMap[K, V]) {
|
return func(csMap *CsMap[K, V]) {}
|
||||||
csMap.shardCount = count
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithCustomHasher[K comparable, V any](h func(key K) uint64) func(csMap *CsMap[K, V]) {
|
func WithCustomHasher[K comparable, V any](h func(key K) uint64) func(csMap *CsMap[K, V]) {
|
||||||
return func(csMap *CsMap[K, V]) {
|
return func(csMap *CsMap[K, V]) {}
|
||||||
csMap.hasher = h
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithSize[K comparable, V any](size uint64) func(csMap *CsMap[K, V]) {
|
func WithSize[K comparable, V any](size uint64) func(csMap *CsMap[K, V]) {
|
||||||
return func(csMap *CsMap[K, V]) {
|
return func(csMap *CsMap[K, V]) {}
|
||||||
csMap.size = size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *CsMap[K, V]) getShard(key K) HashShardPair[K, V] {
|
// -------------------------- 核心操作方法(基于 sync.Map 实现) --------------------------
|
||||||
u := m.hasher(key)
|
|
||||||
return HashShardPair[K, V]{
|
|
||||||
hash: u,
|
|
||||||
shard: m.shards[u%m.shardCount],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Store 存储键值对,兼容原有接口
|
||||||
func (m *CsMap[K, V]) Store(key K, value V) {
|
func (m *CsMap[K, V]) Store(key K, value V) {
|
||||||
hashShardPair := m.getShard(key)
|
m.inner.Store(key, value)
|
||||||
shard := hashShardPair.shard
|
|
||||||
shard.Lock()
|
|
||||||
shard.items.PutWithHash(key, value, hashShardPair.hash)
|
|
||||||
shard.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete 删除指定键,返回是否删除成功
|
||||||
func (m *CsMap[K, V]) Delete(key K) bool {
|
func (m *CsMap[K, V]) Delete(key K) bool {
|
||||||
hashShardPair := m.getShard(key)
|
// sync.Map.Delete 无返回值,需先 Load 判断是否存在
|
||||||
shard := hashShardPair.shard
|
_, ok := m.inner.Load(key)
|
||||||
shard.Lock()
|
if ok {
|
||||||
defer shard.Unlock()
|
m.inner.Delete(key)
|
||||||
return shard.items.DeleteWithHash(key, hashShardPair.hash)
|
}
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteIf 满足条件时删除
|
||||||
func (m *CsMap[K, V]) DeleteIf(key K, condition func(value V) bool) bool {
|
func (m *CsMap[K, V]) DeleteIf(key K, condition func(value V) bool) bool {
|
||||||
hashShardPair := m.getShard(key)
|
// 先 Load 获取值,再判断条件
|
||||||
shard := hashShardPair.shard
|
val, ok := m.inner.Load(key)
|
||||||
shard.Lock()
|
if !ok {
|
||||||
defer shard.Unlock()
|
return false
|
||||||
value, ok := shard.items.GetWithHash(key, hashShardPair.hash)
|
}
|
||||||
if ok && condition(value) {
|
|
||||||
return shard.items.DeleteWithHash(key, hashShardPair.hash)
|
v, okCast := val.(V)
|
||||||
|
if !okCast {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if condition(v) {
|
||||||
|
m.inner.Delete(key)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load 获取指定键的值
|
||||||
func (m *CsMap[K, V]) Load(key K) (V, bool) {
|
func (m *CsMap[K, V]) Load(key K) (V, bool) {
|
||||||
hashShardPair := m.getShard(key)
|
var zero V
|
||||||
shard := hashShardPair.shard
|
val, ok := m.inner.Load(key)
|
||||||
shard.RLock()
|
if !ok {
|
||||||
defer shard.RUnlock()
|
return zero, false
|
||||||
return shard.items.GetWithHash(key, hashShardPair.hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 类型断言(保证类型安全)
|
||||||
|
v, okCast := val.(V)
|
||||||
|
if !okCast {
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has 判断键是否存在
|
||||||
func (m *CsMap[K, V]) Has(key K) bool {
|
func (m *CsMap[K, V]) Has(key K) bool {
|
||||||
hashShardPair := m.getShard(key)
|
_, ok := m.inner.Load(key)
|
||||||
shard := hashShardPair.shard
|
return ok
|
||||||
shard.RLock()
|
|
||||||
defer shard.RUnlock()
|
|
||||||
return shard.items.HasWithHash(key, hashShardPair.hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear 清空所有数据
|
||||||
func (m *CsMap[K, V]) Clear() {
|
func (m *CsMap[K, V]) Clear() {
|
||||||
for i := range m.shards {
|
// sync.Map 无直接 Clear 方法,通过 Range 遍历删除
|
||||||
shard := m.shards[i]
|
m.inner.Range(func(key, value any) bool {
|
||||||
|
m.inner.Delete(key)
|
||||||
shard.Lock()
|
return true
|
||||||
shard.items.Clear()
|
})
|
||||||
shard.Unlock()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count 统计元素数量
|
||||||
func (m *CsMap[K, V]) Count() int {
|
func (m *CsMap[K, V]) Count() int {
|
||||||
count := 0
|
count := 0
|
||||||
for i := range m.shards {
|
m.inner.Range(func(key, value any) bool {
|
||||||
shard := m.shards[i]
|
count++
|
||||||
shard.RLock()
|
return true
|
||||||
count += shard.items.Count()
|
})
|
||||||
shard.RUnlock()
|
|
||||||
}
|
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetIfAbsent 仅当键不存在时设置值
|
||||||
func (m *CsMap[K, V]) SetIfAbsent(key K, value V) {
|
func (m *CsMap[K, V]) SetIfAbsent(key K, value V) {
|
||||||
hashShardPair := m.getShard(key)
|
m.inner.LoadOrStore(key, value)
|
||||||
shard := hashShardPair.shard
|
|
||||||
shard.Lock()
|
|
||||||
_, ok := shard.items.GetWithHash(key, hashShardPair.hash)
|
|
||||||
if !ok {
|
|
||||||
shard.items.PutWithHash(key, value, hashShardPair.hash)
|
|
||||||
}
|
|
||||||
shard.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *CsMap[K, V]) SetIf(key K, conditionFn func(previousVale V, previousFound bool) (value V, set bool)) {
|
// SetIf 根据条件设置值
|
||||||
hashShardPair := m.getShard(key)
|
func (m *CsMap[K, V]) SetIf(key K, conditionFn func(previousValue V, previousFound bool) (value V, set bool)) {
|
||||||
shard := hashShardPair.shard
|
prevVal, found := m.inner.Load(key)
|
||||||
shard.Lock()
|
var prevV V
|
||||||
value, found := shard.items.GetWithHash(key, hashShardPair.hash)
|
if found {
|
||||||
value, ok := conditionFn(value, found)
|
prevV, _ = prevVal.(V)
|
||||||
if ok {
|
|
||||||
shard.items.PutWithHash(key, value, hashShardPair.hash)
|
|
||||||
}
|
|
||||||
shard.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 执行条件函数
|
||||||
|
newVal, set := conditionFn(prevV, found)
|
||||||
|
if set {
|
||||||
|
m.inner.Store(key, newVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIfPresent 仅当键存在时设置值
|
||||||
func (m *CsMap[K, V]) SetIfPresent(key K, value V) {
|
func (m *CsMap[K, V]) SetIfPresent(key K, value V) {
|
||||||
hashShardPair := m.getShard(key)
|
// 先判断是否存在,再设置
|
||||||
shard := hashShardPair.shard
|
if _, ok := m.inner.Load(key); ok {
|
||||||
shard.Lock()
|
m.inner.Store(key, value)
|
||||||
_, ok := shard.items.GetWithHash(key, hashShardPair.hash)
|
|
||||||
if ok {
|
|
||||||
shard.items.PutWithHash(key, value, hashShardPair.hash)
|
|
||||||
}
|
}
|
||||||
shard.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsEmpty 判断是否为空
|
||||||
func (m *CsMap[K, V]) IsEmpty() bool {
|
func (m *CsMap[K, V]) IsEmpty() bool {
|
||||||
return m.Count() == 0
|
return m.Count() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tuple 保留原有结构体(兼容序列化逻辑)
|
||||||
type Tuple[K comparable, V any] struct {
|
type Tuple[K comparable, V any] struct {
|
||||||
Key K
|
Key K
|
||||||
Val V
|
Val V
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------- 保留所有原有方法(无修改) --------------------------
|
// -------------------------- 关键修复:Range 方法(无锁阻塞风险) --------------------------
|
||||||
// 注:以下方法和你的源码完全一致,仅省略实现(避免冗余)
|
|
||||||
// New/WithShardCount/WithCustomHasher/WithSize/getShard/Store/Delete/DeleteIf/
|
|
||||||
// Load/Has/Clear/Count/SetIfAbsent/SetIf/SetIfPresent/IsEmpty/MarshalJSON/UnmarshalJSON
|
|
||||||
|
|
||||||
// -------------------------- 核心优化:Range 方法 --------------------------
|
|
||||||
// Range 同步遍历所有分段,无 channel/goroutine/context 开销,保留 panic 恢复和提前终止
|
|
||||||
// 回调签名完全兼容:返回 true 终止遍历
|
|
||||||
func (m *CsMap[K, V]) Range(f func(key K, value V) (stop bool)) {
|
func (m *CsMap[K, V]) Range(f func(key K, value V) (stop bool)) {
|
||||||
// 1. 提前判空:回调为 nil 直接返回
|
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 原子标志:控制是否终止遍历(替代 context)
|
|
||||||
var stopFlag atomic.Bool
|
var stopFlag atomic.Bool
|
||||||
|
|
||||||
// 3. 遍历所有分段(同步执行,无额外 goroutine)
|
// 基于 sync.Map 的 Range 实现,无额外 goroutine/channel
|
||||||
for i := range m.shards {
|
m.inner.Range(func(key, value any) bool {
|
||||||
// 检测终止标志:提前退出,避免无效遍历
|
// 检测终止标志
|
||||||
if stopFlag.Load() {
|
if stopFlag.Load() {
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// 每个分段的遍历逻辑(带 panic 恢复,和原逻辑一致)
|
|
||||||
func(shardIdx int) {
|
|
||||||
// 保留原有的 panic 恢复逻辑
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
cool.Logger.Error(context.TODO(), "csmap Range shard panic 错误:", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
shard := &m.shards[shardIdx]
|
|
||||||
// 加读锁(并发安全,和原逻辑一致)
|
|
||||||
shard.RLock()
|
|
||||||
defer shard.RUnlock() // 延迟释放,避免锁泄漏
|
|
||||||
|
|
||||||
// 跳过空分段:核心优化点(减少无效遍历)
|
|
||||||
if shard.items.Count() == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 遍历当前分段的元素(复用 swiss.Map 的 Iter 方法)
|
|
||||||
shard.items.Iter(func(k K, v V) (stop bool) {
|
|
||||||
// 检测终止标志:终止当前分段遍历
|
|
||||||
if stopFlag.Load() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行用户回调,保留提前终止逻辑
|
|
||||||
if f(k, v) {
|
|
||||||
stopFlag.Store(true) // 设置全局终止标志
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型断言
|
||||||
|
k, okK := key.(K)
|
||||||
|
v, okV := value.(V)
|
||||||
|
if !okK || !okV {
|
||||||
|
return true // 类型不匹配时跳过,继续遍历
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行用户回调
|
||||||
|
if f(k, v) {
|
||||||
|
stopFlag.Store(true)
|
||||||
|
return false // 终止遍历
|
||||||
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
}(i) // 立即执行函数,避免循环变量捕获问题
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Range If the callback function returns true iteration will stop.
|
// -------------------------- 序列化方法(兼容原有逻辑) --------------------------
|
||||||
// func (m *CsMap[K, V]) Range(f func(key K, value V) (stop bool)) {
|
|
||||||
// ch := make(chan Tuple[K, V], m.Count())
|
|
||||||
|
|
||||||
// ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
// defer cancel()
|
|
||||||
|
|
||||||
// listenCompleted := m.listen(f, ch)
|
|
||||||
// m.produce(ctx, ch)
|
|
||||||
// listenCompleted.Wait()
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (m *CsMap[K, V]) MarshalJSON() ([]byte, error) {
|
func (m *CsMap[K, V]) MarshalJSON() ([]byte, error) {
|
||||||
tmp := make(map[K]V, m.Count())
|
tmp := make(map[K]V, m.Count())
|
||||||
m.Range(func(key K, value V) (stop bool) {
|
m.Range(func(key K, value V) (stop bool) {
|
||||||
@@ -289,71 +196,18 @@ func (m *CsMap[K, V]) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *CsMap[K, V]) UnmarshalJSON(b []byte) error {
|
func (m *CsMap[K, V]) UnmarshalJSON(b []byte) error {
|
||||||
tmp := make(map[K]V, m.Count())
|
tmp := make(map[K]V)
|
||||||
|
|
||||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清空原有数据
|
||||||
|
m.Clear()
|
||||||
|
// 批量存储
|
||||||
for key, val := range tmp {
|
for key, val := range tmp {
|
||||||
m.Store(key, val)
|
m.Store(key, val)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *CsMap[K, V]) produce(ctx context.Context, ch chan Tuple[K, V]) {
|
// -------------------------- 移除所有无用的旧方法(produce/listen 等) --------------------------
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(len(m.shards))
|
|
||||||
for i := range m.shards {
|
|
||||||
go func(i int) {
|
|
||||||
defer wg.Done()
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
|
|
||||||
// 1. 打印错误信息
|
|
||||||
|
|
||||||
cool.Logger.Error(context.TODO(), "csmap panic 错误:", err)
|
|
||||||
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
shard := m.shards[i]
|
|
||||||
shard.RLock()
|
|
||||||
shard.items.Iter(func(k K, v V) (stop bool) {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
ch <- Tuple[K, V]{Key: k, Val: v}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
shard.RUnlock()
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(ch)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *CsMap[K, V]) listen(f func(key K, value V) (stop bool), ch chan Tuple[K, V]) *sync.WaitGroup {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
|
|
||||||
// 1. 打印错误信息
|
|
||||||
|
|
||||||
cool.Logger.Error(context.TODO(), " csmap panic 错误:", err)
|
|
||||||
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for t := range ch {
|
|
||||||
if stop := f(t.Key, t.Val); stop {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return &wg
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ type PetBaseConfig struct {
|
|||||||
|
|
||||||
SKill []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'BOSS技能'" json:"skill"`
|
SKill []uint32 `gorm:"type:jsonb;not null;default:'[]';comment:'BOSS技能'" json:"skill"`
|
||||||
|
|
||||||
Remark string `gorm:"comment:'BOSS备注'" json:"remark"`
|
|
||||||
|
|
||||||
// ISMELEE uint32 `gorm:"not null;default:0;comment:'是否乱斗配置'" json:"is_melee"`
|
// ISMELEE uint32 `gorm:"not null;default:0;comment:'是否乱斗配置'" json:"is_melee"`
|
||||||
// // ===================== BOSS奖励规则(Boss_bonus) =====================
|
// // ===================== BOSS奖励规则(Boss_bonus) =====================
|
||||||
// BonusProbability int32 `gorm:"not null;default:0;comment:'打赢BOSS给奖励概率-分子(值域:0-1000,默认0)'" json:"bonus_probability"`
|
// BonusProbability int32 `gorm:"not null;default:0;comment:'打赢BOSS给奖励概率-分子(值域:0-1000,默认0)'" json:"bonus_probability"`
|
||||||
|
|||||||
@@ -14,9 +14,12 @@ type BossConfig struct {
|
|||||||
PetBaseConfig
|
PetBaseConfig
|
||||||
|
|
||||||
MapID int32 `gorm:"not null;index;comment:'所属地图ID'" json:"map_id" description:"地图ID"`
|
MapID int32 `gorm:"not null;index;comment:'所属地图ID'" json:"map_id" description:"地图ID"`
|
||||||
BossID int32 `gorm:"not null;index;comment:'所属BOSSID'" json:"boss_id" description:"BOSSID"`
|
|
||||||
Order int32 `gorm:"not null;comment:'排序'" json:"order" description:"排序"`
|
|
||||||
|
|
||||||
|
Ordernum int32 `gorm:"not null;comment:'排序'" json:"ordernum" description:"排序"`
|
||||||
|
ParentID int32 `gorm:"column:parentId;type:int" json:"parentId"` // 父ID
|
||||||
|
Remark string `gorm:"comment:'BOSS备注'" json:"remark"`
|
||||||
|
//是否可捕捉MapPit
|
||||||
|
IsCapture int `gorm:"type:int;default:0;comment:'是否可捕捉'" json:"is_capture"`
|
||||||
Script string `gorm:"size:1024;default:'';comment:'BOSS脚本'" json:"script"` //boss出招逻辑做成js脚本
|
Script string `gorm:"size:1024;default:'';comment:'BOSS脚本'" json:"script"` //boss出招逻辑做成js脚本
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"blazing/cool"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
TableNameMapBoss = "config_map_boss" // 地图BOSS配置表(记录BOSS归属、属性、刷新规则、奖励等)
|
|
||||||
)
|
|
||||||
|
|
||||||
// MapBoss 地图BOSS核心配置模型(完全仿照MapPit实现风格)
|
|
||||||
type MapBoss struct {
|
|
||||||
*BaseConfig // 复用通用基础配置(ID/创建时间/更新时间/删除时间/备注等)
|
|
||||||
*Event // 嵌入BOSS事件配置
|
|
||||||
MapID int32 `gorm:"not null;index;comment:'所属地图ID'" json:"map_id" description:"地图ID"`
|
|
||||||
// BOSS唯一标识ID
|
|
||||||
BossID int `gorm:"not null;index;comment:'BOSSID'" json:"boss_id"`
|
|
||||||
BossName string `gorm:"type:varchar(100);default:'';comment:'BOSS名称'" json:"boss_name" description:"BOSS名称"`
|
|
||||||
|
|
||||||
WinBonusID int `gorm:"type:int;default:0;comment:'胜利奖励ID'" json:"win_bonus_id"`
|
|
||||||
FailBonusID int `gorm:"type:int;default:0;comment:'失败奖励ID'" json:"fail_bonus_id"`
|
|
||||||
//是否可捕捉MapPit
|
|
||||||
IsCapture int `gorm:"type:int;default:0;comment:'是否可捕捉'" json:"is_capture"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TableName 指定MapBoss对应的数据库表名(遵循原模型规范)
|
|
||||||
func (*MapBoss) TableName() string {
|
|
||||||
return TableNameMapBoss
|
|
||||||
}
|
|
||||||
|
|
||||||
// GroupName 指定表所属的分组(保持和MapPit一致)
|
|
||||||
func (*MapBoss) GroupName() string {
|
|
||||||
return "default"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMapBoss 创建一个新的MapBoss实例(初始化通用BaseConfig和BossEvent)
|
|
||||||
func NewMapBoss() *MapBoss {
|
|
||||||
return &MapBoss{
|
|
||||||
BaseConfig: NewBaseConfig(),
|
|
||||||
Event: &Event{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// init 初始化表结构(程序启动时自动创建/同步表)
|
|
||||||
func init() {
|
|
||||||
cool.CreateTable(&MapBoss{})
|
|
||||||
}
|
|
||||||
@@ -22,26 +22,26 @@ const (
|
|||||||
// MapNode 地图节点配置模型
|
// MapNode 地图节点配置模型
|
||||||
type MapNode struct {
|
type MapNode struct {
|
||||||
*BaseConfig
|
*BaseConfig
|
||||||
|
*Event // 嵌入BOSS事件配置
|
||||||
// 基础关联字段
|
// 基础关联字段
|
||||||
MapID uint32 `gorm:"not null;index;comment:'所属地图ID'" json:"map_id" description:"地图ID"`
|
NodeType int `gorm:"not null;default:0;comment:'节点类型(0是游戏自带分支,其余自增)'" json:"node_type" description:"节点类型"`
|
||||||
|
NodeID uint32 `gorm:"not null;default:0;comment:'节点ID'" json:"node_id" description:"节点ID"`
|
||||||
|
|
||||||
NodeName string `gorm:"type:varchar(100);default:'';comment:'节点名称'" json:"node_name" description:"节点名称"`
|
NodeName string `gorm:"type:varchar(100);default:'';comment:'节点名称'" json:"node_name" description:"节点名称"`
|
||||||
//节点激活脚本
|
//节点激活脚本
|
||||||
// NodeActiveScript string `gorm:"type:text;comment:'节点激活脚本'" json:"node_active_script" description:"节点激活脚本"`
|
|
||||||
//
|
|
||||||
|
|
||||||
// 节点核心配置
|
|
||||||
NodeType int `gorm:"not null;default:0;comment:'节点类型(1-BOSS,2-NPC,3-场景触发,4-传送门)'" json:"node_type" description:"节点类型"`
|
|
||||||
PositionX float64 `gorm:"not null;default:0;comment:'节点X坐标'" json:"position_x" description:"X坐标"`
|
PositionX float64 `gorm:"not null;default:0;comment:'节点X坐标'" json:"position_x" description:"X坐标"`
|
||||||
PositionY float64 `gorm:"not null;default:0;comment:'节点Y坐标'" json:"position_y" description:"Y坐标"`
|
PositionY float64 `gorm:"not null;default:0;comment:'节点Y坐标'" json:"position_y" description:"Y坐标"`
|
||||||
|
|
||||||
|
WinBonusID int `gorm:"type:int;default:0;comment:'胜利奖励ID'" json:"win_bonus_id"`
|
||||||
|
FailBonusID int `gorm:"type:int;default:0;comment:'失败奖励ID'" json:"fail_bonus_id"`
|
||||||
// 剧情相关配置
|
// 剧情相关配置
|
||||||
TriggerPlotID uint32 `gorm:"default:0;comment:'触发剧情ID(0表示无剧情)'" json:"trigger_plot_id" description:"触发剧情ID"`
|
TriggerPlotID uint32 `gorm:"default:0;comment:'触发剧情ID(0表示无剧情)'" json:"trigger_plot_id" description:"触发剧情ID"`
|
||||||
BindPlotIDs []uint32 `gorm:"type:int[];comment:'绑定的剧情ID列表'" json:"bind_plot_ids" description:"绑定剧情ID列表"`
|
//BindPlotIDs []uint32 `gorm:"type:int[];comment:'绑定的剧情ID列表'" json:"bind_plot_ids" description:"绑定剧情ID列表"`
|
||||||
//完成后的脚本回调,比如战胜和击败绑定不同的任务ID,以及剧情绑定不同的ID
|
//完成后的脚本回调,比如战胜和击败绑定不同的任务ID,以及剧情绑定不同的ID
|
||||||
PlotFinishScript string `gorm:"type:text;comment:'剧情完成后脚本回调'" json:"plot_finish_script" description:"剧情完成后脚本回调"`
|
//回调通boss打完给前端发送固定事件
|
||||||
|
//PlotFinishScript string `gorm:"type:text;comment:'剧情完成后脚本回调'" json:"plot_finish_script" description:"剧情完成后脚本回调"`
|
||||||
|
|
||||||
// BOSS相关配置
|
|
||||||
BindBossID uint32 `gorm:"default:0;comment:'绑定的BOSS ID(0表示无BOSS)'" json:"bind_boss_id" description:"绑定BOSS ID"`
|
BindBossID uint32 `gorm:"default:0;comment:'绑定的BOSS ID(0表示无BOSS)'" json:"bind_boss_id" description:"绑定BOSS ID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ const (
|
|||||||
TableNameBraveTowerConfig = "config_tower_600" // 勇者之塔600配置表
|
TableNameBraveTowerConfig = "config_tower_600" // 勇者之塔600配置表
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------------------------- 核心基类:所有塔配置的通用结构 --------------------------
|
|
||||||
type BaseTowerConfig struct {
|
type BaseTowerConfig struct {
|
||||||
*BaseConfig
|
*BaseConfig
|
||||||
|
Name string `gorm:"type:varchar(100);default:'';comment:'塔名称'" json:"name" description:"塔名称"`
|
||||||
TowerLevel uint32 `gorm:"not null;default:0;uniqueIndex;comment:'塔层数(唯一标识每层配置)'" json:"tower_level" description:"塔层数"`
|
TowerLevel uint32 `gorm:"not null;default:0;uniqueIndex;comment:'塔层数'" json:"tower_level" `
|
||||||
BossIds []uint32 `gorm:"not null;type:jsonb;default:'[]';comment:'绑定BOSS ID数组,关联config_pet_boss表主键'" json:"boss_ids" description:"绑定BOSS数组"`
|
BossIds []uint32 `gorm:"not null;type:jsonb;default:'[]';comment:'绑定BOSS ID数组'" json:"boss_ids" `
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBaseTowerConfig 创建基础塔配置实例(所有塔类型共用)
|
// NewBaseTowerConfig 创建基础塔配置实例(所有塔类型共用)
|
||||||
@@ -26,9 +25,6 @@ func NewBaseTowerConfig() *BaseTowerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------- 各塔类型专属配置模型(无额外字段,仅绑定不同表名)--------------------------
|
|
||||||
|
|
||||||
// Tower110Config 勇者之塔110配置模型
|
|
||||||
type Tower110Config struct {
|
type Tower110Config struct {
|
||||||
*BaseTowerConfig
|
*BaseTowerConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ func NewBossService() *BossService {
|
|||||||
Model: model.NewBossConfig(),
|
Model: model.NewBossConfig(),
|
||||||
PageQueryOp: &cool.QueryOp{
|
PageQueryOp: &cool.QueryOp{
|
||||||
KeyWordField: []string{"desc"},
|
KeyWordField: []string{"desc"},
|
||||||
FieldEQ: []string{"boss_id", "map_id"},
|
FieldEQ: []string{"map_id"},
|
||||||
|
},
|
||||||
|
ListQueryOp: &cool.QueryOp{
|
||||||
|
KeyWordField: []string{"desc"},
|
||||||
|
FieldEQ: []string{"map_id", "parentId"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ func NewMapService() *MapService {
|
|||||||
Model: model.NewMapConfig(),
|
Model: model.NewMapConfig(),
|
||||||
PageQueryOp: &cool.QueryOp{
|
PageQueryOp: &cool.QueryOp{
|
||||||
KeyWordField: []string{"remake"},
|
KeyWordField: []string{"remake"},
|
||||||
|
FieldEQ: []string{"map_id"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type MapBossService struct {
|
|||||||
func NewMapBossService() *MapBossService {
|
func NewMapBossService() *MapBossService {
|
||||||
return &MapBossService{
|
return &MapBossService{
|
||||||
&cool.Service{
|
&cool.Service{
|
||||||
Model: model.NewMapBoss(),
|
Model: model.NewMapNode(),
|
||||||
PageQueryOp: &cool.QueryOp{
|
PageQueryOp: &cool.QueryOp{
|
||||||
KeyWordField: []string{"remake"},
|
KeyWordField: []string{"remake"},
|
||||||
FieldEQ: []string{"map_id"},
|
FieldEQ: []string{"map_id"},
|
||||||
|
|||||||
Reference in New Issue
Block a user