外部服务错误处理修复说明
问题描述
在外部服务(WestDex、Yushan、Zhicha)中,使用 fmt.Errorf("%w: %s", ErrXXX, err) 包装错误后,外层的 errors.Is(err, ErrXXX) 无法正确识别错误类型。
问题原因
fmt.Errorf 创建的包装错误虽然实现了 Unwrap() 接口,但没有实现 Is() 接口,因此 errors.Is 无法正确判断错误类型。
修复方案
统一使用 errors.Join 来组合错误,这是 Go 1.20+ 的标准做法,天然支持 errors.Is 判断。
修复内容
1. WestDex 服务 (westdex_service.go)
修复前:
// 无法被 errors.Is 识别的错误包装
err = fmt.Errorf("%w: %s", ErrSystem, marshalErr.Error())
err = fmt.Errorf("%w: %s", ErrDatasource, westDexResp.Message)
修复后:
// 可以被 errors.Is 正确识别的错误组合
err = errors.Join(ErrSystem, marshalErr)
err = errors.Join(ErrDatasource, fmt.Errorf(westDexResp.Message))
2. Yushan 服务 (yushan_service.go)
修复前:
// 无法被 errors.Is 识别的错误包装
err = fmt.Errorf("%w: %s", ErrSystem, err.Error())
err = fmt.Errorf("%w: %s", ErrDatasource, "羽山请求retdata为空")
修复后:
// 可以被 errors.Is 正确识别的错误组合
err = errors.Join(ErrSystem, err)
err = errors.Join(ErrDatasource, fmt.Errorf("羽山请求retdata为空"))
3. Zhicha 服务 (zhicha_service.go)
修复前:
// 无法被 errors.Is 识别的错误包装
err = fmt.Errorf("%w: %s", ErrSystem, marshalErr.Error())
err = fmt.Errorf("%w: %s", ErrDatasource, "HTTP状态码 %d", response.StatusCode)
修复后:
// 可以被 errors.Is 正确识别的错误组合
err = errors.Join(ErrSystem, marshalErr)
err = errors.Join(ErrDatasource, fmt.Errorf("HTTP状态码 %d", response.StatusCode))
修复效果
修复前的问题:
// 在应用服务层
if errors.Is(err, westdex.ErrDatasource) {
// 这里无法正确识别,因为 fmt.Errorf 包装的错误
// 没有实现 Is() 接口
return ErrDatasource
}
修复后的效果:
// 在应用服务层
if errors.Is(err, westdex.ErrDatasource) {
// 现在可以正确识别了!
return ErrDatasource
}
if errors.Is(err, westdex.ErrSystem) {
// 系统错误也能正确识别
return ErrSystem
}
优势
- 完全兼容:
errors.Is现在可以正确识别所有错误类型 - 标准做法:使用 Go 1.20+ 的
errors.Join标准库功能 - 性能优秀:标准库实现,性能优于自定义解决方案
- 维护简单:无需自定义错误类型,代码更简洁
注意事项
- Go版本要求:需要 Go 1.20 或更高版本(项目使用 Go 1.23.4,完全满足)
- 错误消息格式:
errors.Join使用换行符分隔多个错误 - 向后兼容:现有的错误处理代码无需修改
测试验证
所有修复后的外部服务都能正确编译:
go build ./internal/infrastructure/external/westdex/...
go build ./internal/infrastructure/external/yushan/...
go build ./internal/infrastructure/external/zhicha/...
总结
通过统一使用 errors.Join 修复外部服务的错误处理,现在:
- ✅
errors.Is(err, ErrDatasource)可以正确识别数据源异常 - ✅
errors.Is(err, ErrSystem)可以正确识别系统异常 - ✅
errors.Is(err, ErrNotFound)可以正确识别查询为空 - ✅ 错误处理逻辑更加清晰和可靠
- ✅ 符合 Go 1.20+ 的最佳实践
这个修复确保了整个系统的错误处理链路都能正确工作,提高了系统的可靠性和可维护性。