From 97666485363aeb6365eef54fa055646bb88ac35d Mon Sep 17 00:00:00 2001 From: hoteas <925970985@qq.com> Date: Thu, 10 Apr 2025 22:09:53 +0800 Subject: [PATCH] =?UTF-8?q?refactor(cache):=20=E9=87=8D=E6=9E=84=20CacheMe?= =?UTF-8?q?mory=20=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 sync.Map替代自定义 Map 结构- 优化缓存过期处理逻辑 - 改进通配符删除功能 - 随机触发缓存清理机制 --- application.go | 12 +-- cache/cache_memory.go | 173 ++++++++++++++++-------------------------- code/makecode.go | 2 +- db/hotimedb.go | 50 +++++++++++- dri/baidu/map.go | 90 ++++++++++++++++++++++ 5 files changed, 210 insertions(+), 117 deletions(-) diff --git a/application.go b/application.go index 07e1b0a..9c9cd2f 100644 --- a/application.go +++ b/application.go @@ -285,7 +285,7 @@ func (that *Application) SetConnectListener(lis func(that *Context) (isFinished // //} -//序列化链接 +// 序列化链接 func (that *Application) urlSer(url string) (string, []string) { q := strings.Index(url, "?") if q == -1 { @@ -467,6 +467,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) { if context.Config.GetString("crossDomain") == "" { if sessionId != "" { http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"}) + //context.Resp.Header().Set("Set-Cookie", that.Config.GetString("sessionName")+"="+sessionId+"; Path=/; SameSite=None; Secure") } return @@ -496,7 +497,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) { header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE") header.Set("Access-Control-Allow-Credentials", "true") header.Set("Access-Control-Expose-Headers", "*") - header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token") + header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token,Authorization,Cookie,Set-Cookie") if sessionId != "" { //跨域允许需要设置cookie的允许跨域https才有效果 @@ -511,7 +512,8 @@ func (that *Application) crossDomain(context *Context, sessionId string) { if (origin != "" && strings.Contains(origin, remoteHost)) || strings.Contains(refer, remoteHost) { if sessionId != "" { - http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"}) + //http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"}) + context.Resp.Header().Set("Set-Cookie", that.Config.GetString("sessionName")+"="+sessionId+"; Path=/; SameSite=None; Secure") } return @@ -542,7 +544,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) { header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE") header.Set("Access-Control-Allow-Credentials", "true") header.Set("Access-Control-Expose-Headers", "*") - header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token") + header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token,Authorization,Cookie,Set-Cookie") if sessionId != "" { //跨域允许需要设置cookie的允许跨域https才有效果 @@ -551,7 +553,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) { } -//Init 初始化application +// Init 初始化application func Init(config string) *Application { appIns := Application{} //手动模式, diff --git a/cache/cache_memory.go b/cache/cache_memory.go index 75e87c5..b656a95 100644 --- a/cache/cache_memory.go +++ b/cache/cache_memory.go @@ -7,14 +7,13 @@ import ( "time" ) +// CacheMemory 基于 sync.Map 的缓存实现 type CacheMemory struct { TimeOut int64 DbSet bool SessionSet bool - Map *Error - ContextBase - mutex *sync.RWMutex + cache sync.Map // 替代传统的 Map } func (that *CacheMemory) GetError() *Error { @@ -26,133 +25,91 @@ func (that *CacheMemory) GetError() *Error { func (that *CacheMemory) SetError(err *Error) { that.Error = err } +func (c *CacheMemory) get(key string) (res *Obj) { -//获取Cache键只能为string类型 -func (that *CacheMemory) get(key string) interface{} { - that.Error.SetError(nil) - if that.Map == nil { - that.Map = Map{} + res = &Obj{ + Error: *c.Error, + } + value, ok := c.cache.Load(key) + if !ok { + return res // 缓存不存在 } - if that.Map[key] == nil { - return nil - } - data := that.Map.Get(key, that.Error).(cacheData) - if that.Error.GetError() != nil { - return nil - } + data := value.(cacheData) + // 检查是否过期 if data.time < time.Now().Unix() { - delete(that.Map, key) - return nil + c.cache.Delete(key) // 删除过期缓存 + return res } - return data.data + res.Data = data.data + return res } - -func (that *CacheMemory) refreshMap() { - +func (c *CacheMemory) set(key string, value interface{}, expireAt int64) { + data := cacheData{ + data: value, + time: expireAt, + } + c.cache.Store(key, data) +} +func (c *CacheMemory) delete(key string) { + if strings.Contains(key, "*") { + // 通配符删除 + prefix := strings.TrimSuffix(key, "*") + c.cache.Range(func(k, v interface{}) bool { + if strings.HasPrefix(k.(string), prefix) { + c.cache.Delete(k) + } + return true + }) + } else { + // 精确删除 + c.cache.Delete(key) + } +} +func (c *CacheMemory) refreshMap() { go func() { - that.mutex.Lock() - defer that.mutex.Unlock() - for key, v := range that.Map { - data := v.(cacheData) - if data.time <= time.Now().Unix() { - delete(that.Map, key) + now := time.Now().Unix() + c.cache.Range(func(key, value interface{}) bool { + data := value.(cacheData) + if data.time <= now { + c.cache.Delete(key) // 删除过期缓存 } - } - + return true + }) }() - } +func (c *CacheMemory) Cache(key string, data ...interface{}) *Obj { + now := time.Now().Unix() -//key value ,时间为时间戳 -func (that *CacheMemory) set(key string, value interface{}, time int64) { - that.Error.SetError(nil) - var data cacheData - - if that.Map == nil { - that.Map = Map{} + // 随机触发刷新 + if x := RandX(1, 100000); x > 99950 { + c.refreshMap() } - dd := that.Map[key] - - if dd == nil { - data = cacheData{} - } else { - data = dd.(cacheData) - } - - data.time = time - data.data = value - - that.Map.Put(key, data) -} - -func (that *CacheMemory) delete(key string) { - del := strings.Index(key, "*") - //如果通配删除 - if del != -1 { - key = Substr(key, 0, del) - for k, _ := range that.Map { - if strings.Index(k, key) != -1 { - delete(that.Map, k) - - } - } - - } else { - delete(that.Map, key) - } - -} - -func (that *CacheMemory) Cache(key string, data ...interface{}) *Obj { - - x := RandX(1, 100000) - if x > 99950 { - that.refreshMap() - } - if that.mutex == nil { - that.mutex = &sync.RWMutex{} - } - - reData := &Obj{Data: nil} - if len(data) == 0 { - that.mutex.RLock() - reData.Data = that.get(key) - that.mutex.RUnlock() - return reData + // 读操作 + return c.get(key) } - tim := time.Now().Unix() if len(data) == 1 && data[0] == nil { - that.mutex.Lock() - that.delete(key) - that.mutex.Unlock() - return reData + // 删除操作 + c.delete(key) + return nil } - if len(data) == 1 { - - tim = tim + that.TimeOut - - } + // 写操作 + expireAt := now + c.TimeOut if len(data) == 2 { - that.Error.SetError(nil) - tempt := ObjToInt64(data[1], that.Error) - - if tempt > tim { - - tim = tempt - } else if that.Error.GetError() == nil { - - tim = tim + tempt + if customExpire, ok := data[1].(int64); ok { + if customExpire > now { + expireAt = customExpire + } else { + expireAt = now + customExpire + } } } - that.mutex.Lock() - that.set(key, data[0], tim) - that.mutex.Unlock() - return reData + c.set(key, data[0], expireAt) + return nil } diff --git a/code/makecode.go b/code/makecode.go index 98e8168..1670f5f 100644 --- a/code/makecode.go +++ b/code/makecode.go @@ -158,7 +158,7 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) { tableInfo := make([]Map, 0) if db.Type == "mysql" { - tableInfo = db.Select("INFORMATION_SCHEMA.COLUMNS", "COLUMN_NAME AS name,COLUMN_TYPE AS type,COLUMN_COMMENT AS label,IS_NULLABLE AS must,COLUMN_DEFAULT AS dflt_value", Map{"AND": Map{"TABLE_SCHEMA": db.DBName, "TABLE_NAME": v.GetString("name")}}) + tableInfo = db.Select("INFORMATION_SCHEMA.COLUMNS", "COLUMN_NAME AS name,COLUMN_TYPE AS type,COLUMN_COMMENT AS label,IS_NULLABLE AS must,COLUMN_DEFAULT AS dflt_value", Map{"AND": Map{"TABLE_SCHEMA": db.DBName, "TABLE_NAME": v.GetString("name")}, "ORDER": "ORDINAL_POSITION"}) } if db.Type == "sqlite" { tableInfo = db.Query("pragma table_info([" + v.GetString("name") + "]);") diff --git a/db/hotimedb.go b/db/hotimedb.go index 7a78b6f..b5272d8 100644 --- a/db/hotimedb.go +++ b/db/hotimedb.go @@ -276,7 +276,10 @@ func (that *HoTimeDB) Action(action func(db HoTimeDB) (isSuccess bool)) (isSucce that.Prefix, that.LastQuery, that.LastData, that.ConnectFunc, that.LastErr, that.limit, that.Tx, that.SlaveDB, that.Mode} + + //tx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelReadCommitted}) tx, err := db.Begin() + if err != nil { that.LastErr.SetError(err) return isSuccess @@ -559,6 +562,24 @@ func (that *HoTimeDB) Query(query string, args ...interface{}) []Map { that.LastErr.SetError(err) return nil } + for key, _ := range args { + arg := args[key] + argType := reflect.ValueOf(arg).Type().String() + if strings.Contains(argType, "[]") || strings.Contains(argType, "Slice") { + argLis := ObjToSlice(arg) + //将slice转为,分割字符串 + argStr := "" + for i := 0; i < len(argLis); i++ { + if i == len(argLis)-1 { + argStr += ObjToStr(argLis[i]) + } else { + argStr += ObjToStr(argLis[i]) + "," + } + } + args[key] = argStr + } + + } if that.Tx != nil { resl, err = that.Tx.Query(query, args...) @@ -601,6 +622,29 @@ func (that *HoTimeDB) Exec(query string, args ...interface{}) (sql.Result, *Erro return nil, that.LastErr } + for key, _ := range args { + arg := args[key] + argType := "" + if arg != nil { + argType = reflect.ValueOf(arg).Type().String() + } + + if strings.Contains(argType, "[]") || strings.Contains(argType, "Slice") { + argLis := ObjToSlice(arg) + //将slice转为,分割字符串 + argStr := "" + for i := 0; i < len(argLis); i++ { + if i == len(argLis)-1 { + argStr += ObjToStr(argLis[i]) + } else { + argStr += ObjToStr(argLis[i]) + "," + } + } + args[key] = argStr + } + + } + if that.Tx != nil { resl, e = that.Tx.Exec(query, args...) } else { @@ -874,7 +918,7 @@ func (that *HoTimeDB) Sum(table string, column string, qu ...interface{}) float6 } -//where语句解析 +// where语句解析 func (that *HoTimeDB) where(data Map) (string, []interface{}) { where := "" @@ -1368,7 +1412,7 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) { return where, res } -// that.Db.Update("user",hotime.Map{"ustate":"1"},hotime.Map{"AND":hotime.Map{"OR":hotime.Map{"uid":4,"uname":"dasda"}},"ustate":1}) +// that.Db.Update("user",hotime.Map{"ustate":"1"},hotime.Map{"AND":hotime.Map{"OR":hotime.Map{"uid":4,"uname":"dasda"}},"ustate":1}) func (that *HoTimeDB) notIn(k string, v interface{}, where string, res []interface{}) (string, []interface{}) { //where:="" //fmt.Println(reflect.ValueOf(v).Type().String()) @@ -1504,7 +1548,7 @@ func (that *HoTimeDB) Update(table string, data Map, where Map) int64 { func (that *HoTimeDB) Delete(table string, data map[string]interface{}) int64 { - query := "DELETE FROM " + that.Prefix + table + " " + query := "DELETE FROM `" + that.Prefix + table + "` " temp, resWhere := that.where(data) query += temp + ";" diff --git a/dri/baidu/map.go b/dri/baidu/map.go index c5c372a..c07e0b1 100644 --- a/dri/baidu/map.go +++ b/dri/baidu/map.go @@ -1,6 +1,7 @@ package baidu import ( + . "co "fmt" "io/ioutil" "net/http" @@ -21,6 +22,95 @@ func (that *baiduMap) Init(Ak string) { //query } +// from 源坐标类型: +// 1:GPS标准坐标; +// 2:搜狗地图坐标; +// 3:火星坐标(gcj02),即高德地图、腾讯地图和MapABC等地图使用的坐标; +// 4:3中列举的地图坐标对应的墨卡托平面坐标; +// 5:百度地图采用的经纬度坐标(bd09ll); +// 6:百度地图采用的墨卡托平面坐标(bd09mc); +// 7:图吧地图坐标; +// 8:51地图坐标; +// int 1 1 否 +// to +// 目标坐标类型: +// 3:火星坐标(gcj02),即高德地图、腾讯地图及MapABC等地图使用的坐标; +// 5:百度地图采用的经纬度坐标(bd09ll); +// 6:百度地图采用的墨卡托平面坐标(bd09mc); +func (that *baiduMap) Geoconv(latlngs []Map, from, to int) (Slice, error) { + + client := &http.Client{} + latlngsStr := "" + for _, v := range latlngs { + if latlngsStr != "" { + latlngsStr = latlngsStr + ";" + v.GetString("lng") + "," + v.GetString("lat") + } else { + latlngsStr = v.GetString("lng") + "," + v.GetString("lat") + } + + } + + url := "https://api.map.baidu.com/geoconv/v1/?from=" + ObjToStr(from) + "&to=" + ObjToStr(to) + "&ak=" + that.Ak + "&coords=" + latlngsStr + reqest, err := http.NewRequest("GET", url, nil) + + if err != nil { + fmt.Println("Fatal error ", err.Error()) + return nil, err + } + response, err := client.Do(reqest) + defer response.Body.Close() + + if err != nil { + fmt.Println("Fatal error ", err.Error()) + return nil, err + } + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + + //fmt.Println(string(body)) + data := ObjToMap(string(body)) + if data.GetCeilInt64("status") != 0 { + return nil, err + } + + return data.GetSlice("result"), err +} + +func (that *baiduMap) GetAddress(lat string, lng string) (string, error) { + + client := &http.Client{} + + url := "https://api.map.baidu.com/reverse_geocoding/v3/?ak=" + that.Ak + "&output=json&location=" + lat + "," + lng + reqest, err := http.NewRequest("GET", url, nil) + + if err != nil { + fmt.Println("Fatal error ", err.Error()) + return "", err + } + response, err := client.Do(reqest) + defer response.Body.Close() + + if err != nil { + fmt.Println("Fatal error ", err.Error()) + return "", err + } + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + return "", err + } + + //fmt.Println(string(body)) + + return string(body), err + +} +dy)) + + return string(b // GetPosition 获取定位列表 func (that *baiduMap) GetPosition(name string, region string) (string, error) {