crm/docs/screenshot-bug-fix.md

6.0 KiB
Raw Permalink Blame History

截图存储 Bug 修复说明

问题描述

在实施 Base64 截图存储方案后,发现了一个严重的 Bug

错误信息: GET data:image/jpeg;base64:1 net::ERR_INVALID_URL

根本原因

问题分析

  1. 数据存储方式:使用逗号分隔的字符串存储多个截图

    screenshots := strings.Join(customer.Screenshots, ",")
    
  2. 数据读取方式:使用逗号分割字符串

    c.Screenshots = strings.Split(screenshots.String, ",")
    
  3. Base64 数据特点Base64 编码的字符串本身不包含逗号,但是...

  4. Data URL 格式data:image/jpeg;base64,{base64_string}

    • 注意:逗号是 Data URL 格式的一部分!
    • 格式:data:{MIME类型};base64,{Base64数据}

问题示例

假设有两张截图:

原始数据:

[
  "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
  "data:image/png;base64,iVBORw0KGgoAAAA..."
]

存储到数据库(使用逗号连接):

data:image/jpeg;base64,/9j/4AAQSkZJRg...,data:image/png;base64,iVBORw0KGgoAAAA...

从数据库读取(使用逗号分割):

[
  "data:image/jpeg;base64", // ❌ 被截断!
  "/9j/4AAQSkZJRg...", // ❌ 不是有效的 Data URL
  "data:image/png;base64", // ❌ 被截断!
  "iVBORw0KGgoAAAA..." // ❌ 不是有效的 Data URL
]

结果:前端尝试加载 data:image/jpeg;base64 导致 ERR_INVALID_URL 错误!

解决方案

修改存储格式

逗号分隔字符串改为 JSON 数组

修改前(错误)

// 存储
screenshots := strings.Join(customer.Screenshots, ",")

// 读取
c.Screenshots = strings.Split(screenshots.String, ",")

修改后(正确)

// 存储
screenshotsJSON, err := json.Marshal(customer.Screenshots)
// 结果: ["data:image/jpeg;base64,...","data:image/png;base64,..."]

// 读取
var screenshotArray []string
json.Unmarshal([]byte(screenshots.String), &screenshotArray)

向后兼容

为了兼容旧数据(文件路径格式),添加了降级处理:

if screenshots.Valid && screenshots.String != "" {
    // 尝试解析为 JSON 数组
    var screenshotArray []string
    if err := json.Unmarshal([]byte(screenshots.String), &screenshotArray); err == nil {
        c.Screenshots = screenshotArray
    } else {
        // 向后兼容:如果不是 JSON尝试逗号分隔旧格式
        c.Screenshots = strings.Split(screenshots.String, ",")
    }
} else {
    c.Screenshots = []string{}
}

修改文件清单

/internal/storage/mysql_customer_storage.go

  1. 添加导入

    import "encoding/json"
    
  2. 修改 GetAllCustomers 方法

    • 使用 json.Unmarshal 解析 screenshots 字段
    • 添加向后兼容逻辑
  3. 修改 GetCustomerByID 方法

    • 使用 json.Unmarshal 解析 screenshots 字段
    • 添加向后兼容逻辑
  4. 修改 CreateCustomer 方法

    • 使用 json.Marshal 序列化 screenshots 数组
  5. 修改 UpdateCustomer 方法

    • 使用 json.Marshal 序列化 screenshots 数组

数据格式对比

旧格式(文件路径 - 使用逗号分隔)

数据库存储: /static/uploads/1.jpg,/static/uploads/2.png
解析结果: ["/static/uploads/1.jpg", "/static/uploads/2.png"]
状态: ✅ 正常(路径中没有逗号)

错误格式Base64 - 使用逗号分隔)

数据库存储: data:image/jpeg;base64,abc...,data:image/png;base64,xyz...
解析结果: ["data:image/jpeg;base64", "abc...", "data:image/png;base64", "xyz..."]
状态: ❌ 错误Data URL 被逗号截断)

新格式JSON 数组)

数据库存储: ["data:image/jpeg;base64,abc...","data:image/png;base64,xyz..."]
解析结果: ["data:image/jpeg;base64,abc...", "data:image/png;base64,xyz..."]
状态: ✅ 正常(完整的 Data URL

测试验证

1. 单元测试数据

// 测试 JSON 序列化
screenshots := []string{
    "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
    "data:image/png;base64,iVBORw0KGgoAAAA...",
}

json, _ := json.Marshal(screenshots)
fmt.Println(string(json))
// 输出: ["data:image/jpeg;base64,/9j/4AAQSkZJRg...","data:image/png;base64,iVBORw0KGgoAAAA..."]

// 测试 JSON 反序列化
var result []string
json.Unmarshal(json, &result)
fmt.Println(result)
// 输出: [data:image/jpeg;base64,/9j/4AAQSkZJRg... data:image/png;base64,iVBORw0KGgoAAAA...]

2. 集成测试

# 1. 上传截图
curl -X POST http://localhost:8081/api/upload \
  -F "screenshots=@test.jpg"

# 2. 创建客户
curl -X POST http://localhost:8081/api/customers \
  -H "Content-Type: application/json" \
  -d '{
    "customerName": "测试",
    "screenshots": ["data:image/jpeg;base64,/9j/4AAQ..."]
  }'

# 3. 查询客户
curl http://localhost:8081/api/customers/{id}

# 4. 验证数据库
mysql> SELECT screenshots FROM customers WHERE id = '{id}';
# 应该看到: ["data:image/jpeg;base64,/9j/4AAQ..."]

经验教训

1. 分隔符选择的重要性

当使用分隔符连接字符串时,必须确保:

  • 分隔符不会出现在数据中
  • 或者使用转义机制
  • 或者使用结构化格式(如 JSON

2. Data URL 格式

data:[<mediatype>][;base64],<data>
     ^           ^       ^
     |           |       |
     MIME类型    编码    逗号分隔符(重要!)

逗号是 Data URL 规范的一部分,不能用作数组分隔符!

3. 最佳实践

对于复杂数据结构,优先使用:

  1. JSON - 结构化、标准、易于解析
  2. Protocol Buffers - 高性能、类型安全
  3. 避免自定义分隔符 - 容易出错

4. 向后兼容

在修改数据格式时,务必考虑:

  • 现有数据的迁移
  • 降级处理逻辑
  • 渐进式升级策略

总结

这个 Bug 的根本原因是:使用逗号分隔包含逗号的数据

修复方案:使用 JSON 数组格式存储和解析数据

这是一个典型的数据格式设计问题,提醒我们在设计数据存储格式时,必须充分考虑数据的特性和边界情况。