crm/docs/screenshot-bug-fix.md

251 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 截图存储 Bug 修复说明
## 问题描述
在实施 Base64 截图存储方案后,发现了一个严重的 Bug
```
错误信息: GET data:image/jpeg;base64:1 net::ERR_INVALID_URL
```
## 根本原因
### 问题分析
1. **数据存储方式**:使用逗号分隔的字符串存储多个截图
```go
screenshots := strings.Join(customer.Screenshots, ",")
```
2. **数据读取方式**:使用逗号分割字符串
```go
c.Screenshots = strings.Split(screenshots.String, ",")
```
3. **Base64 数据特点**Base64 编码的字符串**本身不包含逗号**,但是...
4. **Data URL 格式**`data:image/jpeg;base64,{base64_string}`
- 注意:**逗号**是 Data URL 格式的一部分!
- 格式:`data:{MIME类型};base64,{Base64数据}`
### 问题示例
假设有两张截图:
**原始数据**:
```json
[
"data:image/jpeg;base64,/9j/4AAQSkZJRg...",
"data:image/png;base64,iVBORw0KGgoAAAA..."
]
```
**存储到数据库**(使用逗号连接):
```
data:image/jpeg;base64,/9j/4AAQSkZJRg...,data:image/png;base64,iVBORw0KGgoAAAA...
```
**从数据库读取**(使用逗号分割):
```json
[
"data:image/jpeg;base64", // ❌ 被截断!
"/9j/4AAQSkZJRg...", // ❌ 不是有效的 Data URL
"data:image/png;base64", // ❌ 被截断!
"iVBORw0KGgoAAAA..." // ❌ 不是有效的 Data URL
]
```
**结果**:前端尝试加载 `data:image/jpeg;base64` 导致 `ERR_INVALID_URL` 错误!
## 解决方案
### 修改存储格式
从**逗号分隔字符串**改为 **JSON 数组**
#### 修改前(错误)
```go
// 存储
screenshots := strings.Join(customer.Screenshots, ",")
// 读取
c.Screenshots = strings.Split(screenshots.String, ",")
```
#### 修改后(正确)
```go
// 存储
screenshotsJSON, err := json.Marshal(customer.Screenshots)
// 结果: ["data:image/jpeg;base64,...","data:image/png;base64,..."]
// 读取
var screenshotArray []string
json.Unmarshal([]byte(screenshots.String), &screenshotArray)
```
### 向后兼容
为了兼容旧数据(文件路径格式),添加了降级处理:
```go
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. **添加导入**
```go
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. 单元测试数据
```go
// 测试 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. 集成测试
```bash
# 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 数组格式存储和解析数据**。
这是一个典型的**数据格式设计问题**,提醒我们在设计数据存储格式时,必须充分考虑数据的特性和边界情况。