Files
hotime/.cursor/plans/row方法方案审查与修正_66435b7a.plan.md
T
hoteas fab7931d3c refactor(app): 优化 URL 处理和 JSON 解析逻辑
- 更新应用程序处理程序中的 URL 赋值逻辑,确保静态文件使用原始路径
- 修改缓存数据库的 JSON 解析方法,使用 JsonToObj 函数替代 json.Unmarshal,提升代码可读性和性能
- 在 Map 和 Slice 类型中新增获取四舍五入浮点数的方法,增强数据处理能力
- 在 Obj 类型中添加四舍五入功能,支持精度控制
- 改进数据库查询结果的处理逻辑,确保数据类型的准确性和一致性
- 优化日志格式设置,增强日志信息的可读性
2026-03-10 23:44:41 +08:00

9.4 KiB
Raw Blame History

name, overview, todos, isProject
name overview todos isProject
Row方法方案审查与修正 修改 4 个文件:common/objtoobj.go(新增 JsonToObj + 增强 ObjToMap/ObjToSlice)、common/map.go(增强 JsonToMap)、db/query.goRow 重写)、cache/cache_db.go(用 JsonToObj 替换 json.Unmarshal)。
id content status
add-json-safe 在 objtoobj.go 新增 JsonToObj(公共) + fixJsonNumbers(私有),增强 ObjToMap/ObjToSlice 的 4 处 json.Unmarshal completed
id content status
fix-json-to-map 增强 map.go 的 JsonToMap,使用 UseNumber + fixJsonNumbers completed
id content status
fix-row-method 重写 db/query.go 的 Row() + 新增辅助函数 + 更新 import completed
id content status
fix-cache-db cache/cache_db.go 的 3 处 json.Unmarshal 改用 JsonToObj completed
false

Row() + JSON 反序列化类型保留修正方案

修改文件一览

文件 改动
common/objtoobj.go 新增 JsonToObj + fixJsonNumbers;增强 ObjToMapObjToSlice 的 4 处 json.Unmarshal
common/map.go 增强 Map.JsonToMap
db/query.go 重写 Row() + 新增辅助函数 + 更新 import
cache/cache_db.go 3 处 json.Unmarshal 改用 JsonToObj

文件 1common/objtoobj.go

新增 JsonToObj 和 fixJsonNumbers

在文件 StrToMap 之前(约第 378 行前)新增,需要在 import 中添加 "bytes"

// JsonToObj 将 JSON 字符串反序列化为 interface{},保留数字原始类型。
// 标准 json.Unmarshal 会把所有 JSON 数字变成 float64,本函数保留整数为 int64、小数为 float64。
//   JsonToObj(`{"id":2,"price":1.20}`)  → Map{"id": int64(2), "price": float64(1.2)}
//   JsonToObj(`[1, 2.5, "a"]`)          → Slice{int64(1), float64(2.5), "a"}
func JsonToObj(jsonStr string) (interface{}, error) {
    dec := json.NewDecoder(bytes.NewReader([]byte(jsonStr)))
    dec.UseNumber()
    var result interface{}
    if err := dec.Decode(&result); err != nil {
        return nil, err
    }
    return fixJsonNumbers(result), nil
}

// fixJsonNumbers 递归将 json.Number 转为 int64 或 float64
func fixJsonNumbers(v interface{}) interface{} {
    switch val := v.(type) {
    case json.Number:
        if n, err := val.Int64(); err == nil {
            return n
        }
        if f, err := val.Float64(); err == nil {
            return f
        }
        return string(val)
    case map[string]interface{}:
        for k, item := range val {
            val[k] = fixJsonNumbers(item)
        }
        return val
    case []interface{}:
        for i, item := range val {
            val[i] = fixJsonNumbers(item)
        }
        return val
    default:
        return val
    }
}

增强 ObjToMap2 处 json.Unmarshal

位置 1 -- string case(约第 27-31 行):

// 修改前:
case string:
    v = Map{}
    e := json.Unmarshal([]byte(obj.(string)), &v)
    if e != nil {
        err = errors.New("没有合适的转换对象!" + e.Error())
        v = nil
    }

// 修改后:
case string:
    result, e := JsonToObj(val)
    if e != nil {
        err = errors.New("没有合适的转换对象!" + e.Error())
        v = nil
    } else if m, ok := result.(map[string]interface{}); ok {
        v = m
    } else {
        err = errors.New("没有合适的转换对象!")
        v = nil
    }

位置 2 -- default case(约第 34-44 行):

// 修改前:
default:
    data, err := json.Marshal(obj)
    if err != nil { ... }
    v = Map{}
    e := json.Unmarshal(data, &v)
    if e != nil { ... }

// 修改后:
default:
    data, e2 := json.Marshal(obj)
    if e2 != nil {
        err = errors.New("没有合适的转换对象!" + e2.Error())
        v = nil
    } else {
        result, e3 := JsonToObj(string(data))
        if e3 != nil {
            err = errors.New("没有合适的转换对象!" + e3.Error())
            v = nil
        } else if m, ok := result.(map[string]interface{}); ok {
            v = m
        } else {
            err = errors.New("没有合适的转换对象!")
            v = nil
        }
    }

增强 ObjToSlice2 处 json.Unmarshal

位置 1 -- string case(约第 82-83 行):

// 修改前:
case string:
    v = Slice{}
    err = json.Unmarshal([]byte(obj.(string)), &v)

// 修改后:
case string:
    result, e := JsonToObj(val)
    if e != nil {
        err = e
    } else if s, ok := result.([]interface{}); ok {
        v = s
    } else {
        err = errors.New("没有合适的转换对象!")
    }

位置 2 -- default case(约第 86-91 行):

// 修改前:
default:
    v = Slice{}
    var data []byte
    data, err = json.Marshal(obj)
    err = json.Unmarshal(data, &v)

// 修改后:
default:
    data, e2 := json.Marshal(obj)
    if e2 != nil {
        err = e2
    } else {
        result, e3 := JsonToObj(string(data))
        if e3 != nil {
            err = e3
        } else if s, ok := result.([]interface{}); ok {
            v = s
        } else {
            err = errors.New("没有合适的转换对象!")
        }
    }

文件 2common/map.go

增强 JsonToMap(第 183-189 行)

需要在 import 中添加 "bytes"

// 修改前:
func (that Map) JsonToMap(jsonStr string, err ...*Error) {
    e := json.Unmarshal([]byte(jsonStr), &that)
    if e != nil && len(err) != 0 {
        err[0].SetError(e)
    }
}

// 修改后:
func (that Map) JsonToMap(jsonStr string, err ...*Error) {
    dec := json.NewDecoder(bytes.NewReader([]byte(jsonStr)))
    dec.UseNumber()
    e := dec.Decode(&that)
    if e != nil {
        if len(err) != 0 {
            err[0].SetError(e)
        }
        return
    }
    for k, v := range that {
        that[k] = fixJsonNumbers(v)
    }
}

这里 fixJsonNumbers 定义在 objtoobj.go 中,同属 common 包,可以直接调用。

影响链路:

  • StrToMap(s) → 调用 JsonToMap(s)自动修复
  • Map.JsonToMap(s)直接修复

文件 3db/query.go -- Row() 重写

(内容与上一版相同,此处只列关键点)

import 变更

移除 "reflect",新增 "math""strconv"

新增辅助函数

dbTypeUnknown/dbTypeInteger/dbTypeFloat/dbTypeDecimal 常量 + classifyDBType + getDecimalScale + getFloatScale + fixFloatValue + roundFloat + convertBytes

覆盖 MySQL/SQLite/PostgreSQL 三种数据库的类型名。

Row() 方法替换(第 362-391 行)

  • defer resl.Close() + resl.Err() 检查
  • ColumnTypes() 预分类列类型
  • type switch 替代 reflect[]byteconvertBytesfloat64fixFloatValuefloat32fixFloatValue,其余不变

文件 4cache/cache_db.go -- DB 缓存

3 处 json.Unmarshal 改用 JsonToObj(来自 common 包,已通过 . "code.hoteas.com/golang/hotime/common" 导入):

位置 1 -- getLegacy() 约第 359-363 行:

// 修改前:
var data interface{}
err := json.Unmarshal([]byte(valueStr), &data)

// 修改后:
data, err := JsonToObj(valueStr)

位置 2 -- get() 约第 411-416 行:

// 修改前:
var data interface{}
err := json.Unmarshal([]byte(valueStr), &data)

// 修改后:
data, err := JsonToObj(valueStr)

位置 3 -- CachesGet() 约第 594-598 行:

// 修改前:
var data interface{}
err := json.Unmarshal([]byte(valueStr), &data)

// 修改后:
data, err := JsonToObj(valueStr)

不需要新增 importJsonToObj 在 common 包,cache_db.go 已 import)。


修正效果总结

数据路径 修正前 修正后
DB → Row() → Map float32/float64 原样存入,[]byte 全转 string 按列类型精确转换:INT→int64, DECIMAL→roundFloat, FLOAT→fixFloat
DB 缓存 → json.Unmarshal 所有数字→float64 JsonToObj:整数→int64, 小数→float64
ObjToMap(string) json.Unmarshal 数字全→float64 JsonToObj:保留 int64/float64
ObjToSlice(string) json.Unmarshal 数字全→float64 JsonToObj:保留 int64/float64
StrToMap(string) JsonToMap → json.Unmarshal JsonToMap → UseNumber + fixJsonNumbers
Map.JsonToMap(string) json.Unmarshal UseNumber + fixJsonNumbers
Redis 缓存 ObjToStr → string → ObjToMap ObjToMap 已增强,自动修复

Redis 缓存路径:setObjToStrget 返回 string → 调用方用 ObjToMap 转回 Map。由于 ObjToMap 的 string 分支现在使用 JsonToObjRedis 缓存也自动获得类型保留,无需单独改 cache_redis.go。