fab7931d3c
- 更新应用程序处理程序中的 URL 赋值逻辑,确保静态文件使用原始路径 - 修改缓存数据库的 JSON 解析方法,使用 JsonToObj 函数替代 json.Unmarshal,提升代码可读性和性能 - 在 Map 和 Slice 类型中新增获取四舍五入浮点数的方法,增强数据处理能力 - 在 Obj 类型中添加四舍五入功能,支持精度控制 - 改进数据库查询结果的处理逻辑,确保数据类型的准确性和一致性 - 优化日志格式设置,增强日志信息的可读性
311 lines
9.4 KiB
Markdown
311 lines
9.4 KiB
Markdown
---
|
||
name: Row方法方案审查与修正
|
||
overview: 修改 4 个文件:common/objtoobj.go(新增 JsonToObj + 增强 ObjToMap/ObjToSlice)、common/map.go(增强 JsonToMap)、db/query.go(Row 重写)、cache/cache_db.go(用 JsonToObj 替换 json.Unmarshal)。
|
||
todos:
|
||
- id: add-json-safe
|
||
content: 在 objtoobj.go 新增 JsonToObj(公共) + fixJsonNumbers(私有),增强 ObjToMap/ObjToSlice 的 4 处 json.Unmarshal
|
||
status: completed
|
||
- id: fix-json-to-map
|
||
content: 增强 map.go 的 JsonToMap,使用 UseNumber + fixJsonNumbers
|
||
status: completed
|
||
- id: fix-row-method
|
||
content: 重写 db/query.go 的 Row() + 新增辅助函数 + 更新 import
|
||
status: completed
|
||
- id: fix-cache-db
|
||
content: cache/cache_db.go 的 3 处 json.Unmarshal 改用 JsonToObj
|
||
status: completed
|
||
isProject: false
|
||
---
|
||
|
||
# Row() + JSON 反序列化类型保留修正方案
|
||
|
||
## 修改文件一览
|
||
|
||
|
||
| 文件 | 改动 |
|
||
| ------------------ | --------------------------------------------------------------------------------- |
|
||
| common/objtoobj.go | 新增 `JsonToObj` + `fixJsonNumbers`;增强 `ObjToMap`、`ObjToSlice` 的 4 处 json.Unmarshal |
|
||
| common/map.go | 增强 `Map.JsonToMap` |
|
||
| db/query.go | 重写 Row() + 新增辅助函数 + 更新 import |
|
||
| cache/cache_db.go | 3 处 json.Unmarshal 改用 `JsonToObj` |
|
||
|
||
|
||
---
|
||
|
||
## 文件 1:common/objtoobj.go
|
||
|
||
### 新增 JsonToObj 和 fixJsonNumbers
|
||
|
||
在文件 `StrToMap` 之前(约第 378 行前)新增,需要在 import 中添加 `"bytes"`:
|
||
|
||
```go
|
||
// 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
|
||
}
|
||
}
|
||
```
|
||
|
||
### 增强 ObjToMap(2 处 json.Unmarshal)
|
||
|
||
**位置 1** -- string case(约第 27-31 行):
|
||
|
||
```go
|
||
// 修改前:
|
||
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 行):
|
||
|
||
```go
|
||
// 修改前:
|
||
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
|
||
}
|
||
}
|
||
```
|
||
|
||
### 增强 ObjToSlice(2 处 json.Unmarshal)
|
||
|
||
**位置 1** -- string case(约第 82-83 行):
|
||
|
||
```go
|
||
// 修改前:
|
||
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 行):
|
||
|
||
```go
|
||
// 修改前:
|
||
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("没有合适的转换对象!")
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 文件 2:common/map.go
|
||
|
||
### 增强 JsonToMap(第 183-189 行)
|
||
|
||
需要在 import 中添加 `"bytes"`:
|
||
|
||
```go
|
||
// 修改前:
|
||
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)` → **直接修复**
|
||
|
||
---
|
||
|
||
## 文件 3:db/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:`[]byte` → `convertBytes`,`float64` → `fixFloatValue`,`float32` → `fixFloatValue`,其余不变
|
||
|
||
---
|
||
|
||
## 文件 4:cache/cache_db.go -- DB 缓存
|
||
|
||
3 处 `json.Unmarshal` 改用 `JsonToObj`(来自 common 包,已通过 `. "code.hoteas.com/golang/hotime/common"` 导入):
|
||
|
||
**位置 1** -- `getLegacy()` 约第 359-363 行:
|
||
|
||
```go
|
||
// 修改前:
|
||
var data interface{}
|
||
err := json.Unmarshal([]byte(valueStr), &data)
|
||
|
||
// 修改后:
|
||
data, err := JsonToObj(valueStr)
|
||
```
|
||
|
||
**位置 2** -- `get()` 约第 411-416 行:
|
||
|
||
```go
|
||
// 修改前:
|
||
var data interface{}
|
||
err := json.Unmarshal([]byte(valueStr), &data)
|
||
|
||
// 修改后:
|
||
data, err := JsonToObj(valueStr)
|
||
```
|
||
|
||
**位置 3** -- `CachesGet()` 约第 594-598 行:
|
||
|
||
```go
|
||
// 修改前:
|
||
var data interface{}
|
||
err := json.Unmarshal([]byte(valueStr), &data)
|
||
|
||
// 修改后:
|
||
data, err := JsonToObj(valueStr)
|
||
```
|
||
|
||
不需要新增 import(`JsonToObj` 在 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 缓存路径:`set` 用 `ObjToStr` → `get` 返回 string → 调用方用 `ObjToMap` 转回 Map。由于 `ObjToMap` 的 string 分支现在使用 `JsonToObj`,Redis 缓存也**自动获得类型保留**,无需单独改 cache_redis.go。 |