1
0
Files
nex/backend/internal/conversion/engine.go
lanyuanxiaoyao d92db73937 refactor: 后端代码质量优化 - 复用公共库、使用标准库、类型安全错误判断
## 高优先级修复
- stats_service_impl: 使用 strings.SplitN 替代错误的索引分割
- provider_handler: 使用 errors.Is(err, gorm.ErrDuplicatedKey) 替代字符串匹配
- client: 重写 isNetworkError 使用 errors.As/Is 类型安全判断
- proxy_handler: 使用 encoding/json 标准库解析 JSON(extractModelName、isStreamRequest)

## 中优先级修复
- stats_handler: 添加 parseDateParam 辅助函数消除重复日期解析
- pkg/errors: 新增 ErrRequestCreate/Send/ResponseRead 错误类型和 WithCause 方法
- client: 使用结构化错误替代 fmt.Errorf
- ConversionEngine: logger 依赖注入,替换所有 zap.L() 调用

## 低优先级修复
- encoder: 删除 joinStrings,使用 strings.Join
- adapter: 删除 modelInfoRegex 正则,使用 isModelInfoPath 字符串函数

## 文档更新
- README.md: 添加公共库使用指南和编码规范章节
- specs: 同步 delta specs 到 main specs(error-handling、structured-logging、request-validation)

## 归档
- openspec/changes/archive/2026-04-20-refactor-backend-code-quality/
2026-04-20 16:42:48 +08:00

344 lines
11 KiB
Go

package conversion
import (
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"go.uber.org/zap"
)
// HTTPRequestSpec HTTP 请求规格
type HTTPRequestSpec struct {
URL string `json:"url"`
Method string `json:"method"`
Headers map[string]string `json:"headers"`
Body []byte `json:"body"`
}
// HTTPResponseSpec HTTP 响应规格
type HTTPResponseSpec struct {
StatusCode int `json:"status_code"`
Headers map[string]string `json:"headers"`
Body []byte `json:"body"`
}
// ConversionEngine 转换引擎门面
type ConversionEngine struct {
registry AdapterRegistry
middlewareChain *MiddlewareChain
logger *zap.Logger
}
// NewConversionEngine 创建转换引擎
func NewConversionEngine(registry AdapterRegistry, logger *zap.Logger) *ConversionEngine {
if logger == nil {
logger = zap.L()
}
return &ConversionEngine{
registry: registry,
middlewareChain: NewMiddlewareChain(),
logger: logger,
}
}
// RegisterAdapter 注册协议适配器
func (e *ConversionEngine) RegisterAdapter(adapter ProtocolAdapter) error {
return e.registry.Register(adapter)
}
// GetRegistry 返回注册表(供外部使用)
func (e *ConversionEngine) GetRegistry() AdapterRegistry {
return e.registry
}
// Use 添加中间件
func (e *ConversionEngine) Use(mw ConversionMiddleware) {
e.middlewareChain.Use(mw)
}
// IsPassthrough 判断是否同协议透传
func (e *ConversionEngine) IsPassthrough(clientProtocol, providerProtocol string) bool {
if clientProtocol != providerProtocol {
return false
}
adapter, err := e.registry.Get(clientProtocol)
if err != nil {
return false
}
return adapter.SupportsPassthrough()
}
// ConvertHttpRequest 转换 HTTP 请求
func (e *ConversionEngine) ConvertHttpRequest(spec HTTPRequestSpec, clientProtocol, providerProtocol string, provider *TargetProvider) (*HTTPRequestSpec, error) {
nativePath := spec.URL
if e.IsPassthrough(clientProtocol, providerProtocol) {
providerAdapter, err := e.registry.Get(providerProtocol)
if err != nil {
return nil, err
}
return &HTTPRequestSpec{
URL: provider.BaseURL + nativePath,
Method: spec.Method,
Headers: providerAdapter.BuildHeaders(provider),
Body: spec.Body,
}, nil
}
clientAdapter, err := e.registry.Get(clientProtocol)
if err != nil {
return nil, fmt.Errorf("未找到客户端适配器 %s: %w", clientProtocol, err)
}
providerAdapter, err := e.registry.Get(providerProtocol)
if err != nil {
return nil, fmt.Errorf("未找到服务端适配器 %s: %w", providerProtocol, err)
}
interfaceType := clientAdapter.DetectInterfaceType(nativePath)
providerUrl := providerAdapter.BuildUrl(nativePath, interfaceType)
providerHeaders := providerAdapter.BuildHeaders(provider)
providerBody, err := e.convertBody(interfaceType, clientAdapter, providerAdapter, provider, spec.Body)
if err != nil {
return nil, err
}
return &HTTPRequestSpec{
URL: provider.BaseURL + providerUrl,
Method: spec.Method,
Headers: providerHeaders,
Body: providerBody,
}, nil
}
// ConvertHttpResponse 转换 HTTP 响应
func (e *ConversionEngine) ConvertHttpResponse(spec HTTPResponseSpec, clientProtocol, providerProtocol string, interfaceType InterfaceType) (*HTTPResponseSpec, error) {
if e.IsPassthrough(clientProtocol, providerProtocol) {
return &spec, nil
}
clientAdapter, err := e.registry.Get(clientProtocol)
if err != nil {
return nil, err
}
providerAdapter, err := e.registry.Get(providerProtocol)
if err != nil {
return nil, err
}
convertedBody, err := e.convertResponseBody(interfaceType, clientAdapter, providerAdapter, spec.Body)
if err != nil {
return nil, err
}
return &HTTPResponseSpec{
StatusCode: spec.StatusCode,
Headers: spec.Headers,
Body: convertedBody,
}, nil
}
// CreateStreamConverter 创建流式转换器
func (e *ConversionEngine) CreateStreamConverter(clientProtocol, providerProtocol string) (StreamConverter, error) {
if e.IsPassthrough(clientProtocol, providerProtocol) {
return NewPassthroughStreamConverter(), nil
}
providerAdapter, err := e.registry.Get(providerProtocol)
if err != nil {
return nil, err
}
clientAdapter, err := e.registry.Get(clientProtocol)
if err != nil {
return nil, err
}
ctx := ConversionContext{
ConversionID: uuid.New().String(),
InterfaceType: InterfaceTypeChat,
Timestamp: time.Now(),
}
return NewCanonicalStreamConverterWithMiddleware(
providerAdapter.CreateStreamDecoder(),
clientAdapter.CreateStreamEncoder(),
e.middlewareChain,
ctx,
clientProtocol,
providerProtocol,
), nil
}
// convertBody 转换请求体
func (e *ConversionEngine) convertBody(interfaceType InterfaceType, clientAdapter, providerAdapter ProtocolAdapter, provider *TargetProvider, body []byte) ([]byte, error) {
switch interfaceType {
case InterfaceTypeChat:
return e.convertChatBody(clientAdapter, providerAdapter, provider, body)
case InterfaceTypeModels, InterfaceTypeModelInfo:
return body, nil
case InterfaceTypeEmbeddings:
if !clientAdapter.SupportsInterface(InterfaceTypeEmbeddings) || !providerAdapter.SupportsInterface(InterfaceTypeEmbeddings) {
return body, nil
}
return e.convertEmbeddingBody(clientAdapter, providerAdapter, provider, body)
case InterfaceTypeRerank:
if !clientAdapter.SupportsInterface(InterfaceTypeRerank) || !providerAdapter.SupportsInterface(InterfaceTypeRerank) {
return body, nil
}
return e.convertRerankBody(clientAdapter, providerAdapter, provider, body)
default:
return body, nil
}
}
// convertResponseBody 转换响应体
func (e *ConversionEngine) convertResponseBody(interfaceType InterfaceType, clientAdapter, providerAdapter ProtocolAdapter, body []byte) ([]byte, error) {
switch interfaceType {
case InterfaceTypeChat:
return e.convertChatResponseBody(clientAdapter, providerAdapter, body)
case InterfaceTypeModels:
if !clientAdapter.SupportsInterface(InterfaceTypeModels) || !providerAdapter.SupportsInterface(InterfaceTypeModels) {
return body, nil
}
return e.convertModelsResponseBody(clientAdapter, providerAdapter, body)
case InterfaceTypeModelInfo:
if !clientAdapter.SupportsInterface(InterfaceTypeModelInfo) || !providerAdapter.SupportsInterface(InterfaceTypeModelInfo) {
return body, nil
}
return e.convertModelInfoResponseBody(clientAdapter, providerAdapter, body)
case InterfaceTypeEmbeddings:
if !clientAdapter.SupportsInterface(InterfaceTypeEmbeddings) || !providerAdapter.SupportsInterface(InterfaceTypeEmbeddings) {
return body, nil
}
return e.convertEmbeddingResponseBody(clientAdapter, providerAdapter, body)
case InterfaceTypeRerank:
if !clientAdapter.SupportsInterface(InterfaceTypeRerank) || !providerAdapter.SupportsInterface(InterfaceTypeRerank) {
return body, nil
}
return e.convertRerankResponseBody(clientAdapter, providerAdapter, body)
default:
return body, nil
}
}
func (e *ConversionEngine) convertChatBody(clientAdapter, providerAdapter ProtocolAdapter, provider *TargetProvider, body []byte) ([]byte, error) {
canonicalReq, err := clientAdapter.DecodeRequest(body)
if err != nil {
return nil, NewConversionError(ErrorCodeJSONParseError, "解码请求失败").WithCause(err)
}
ctx := NewConversionContext(InterfaceTypeChat)
canonicalReq, err = e.middlewareChain.Apply(canonicalReq, clientAdapter.ProtocolName(), providerAdapter.ProtocolName(), ctx)
if err != nil {
return nil, err
}
encoded, err := providerAdapter.EncodeRequest(canonicalReq, provider)
if err != nil {
return nil, NewConversionError(ErrorCodeEncodingFailure, "编码请求失败").WithCause(err)
}
return encoded, nil
}
func (e *ConversionEngine) convertChatResponseBody(clientAdapter, providerAdapter ProtocolAdapter, body []byte) ([]byte, error) {
canonicalResp, err := providerAdapter.DecodeResponse(body)
if err != nil {
return nil, NewConversionError(ErrorCodeJSONParseError, "解码响应失败").WithCause(err)
}
encoded, err := clientAdapter.EncodeResponse(canonicalResp)
if err != nil {
return nil, NewConversionError(ErrorCodeEncodingFailure, "编码响应失败").WithCause(err)
}
return encoded, nil
}
func (e *ConversionEngine) convertModelsResponseBody(clientAdapter, providerAdapter ProtocolAdapter, body []byte) ([]byte, error) {
models, err := providerAdapter.DecodeModelsResponse(body)
if err != nil {
e.logger.Warn("解码 Models 响应失败,返回原始响应", zap.String("error", err.Error()))
return body, nil
}
encoded, err := clientAdapter.EncodeModelsResponse(models)
if err != nil {
e.logger.Warn("编码 Models 响应失败,返回原始响应", zap.String("error", err.Error()))
return body, nil
}
return encoded, nil
}
func (e *ConversionEngine) convertModelInfoResponseBody(clientAdapter, providerAdapter ProtocolAdapter, body []byte) ([]byte, error) {
info, err := providerAdapter.DecodeModelInfoResponse(body)
if err != nil {
e.logger.Warn("解码 ModelInfo 响应失败,返回原始响应", zap.String("error", err.Error()))
return body, nil
}
encoded, err := clientAdapter.EncodeModelInfoResponse(info)
if err != nil {
e.logger.Warn("编码 ModelInfo 响应失败,返回原始响应", zap.String("error", err.Error()))
return body, nil
}
return encoded, nil
}
func (e *ConversionEngine) convertEmbeddingBody(clientAdapter, providerAdapter ProtocolAdapter, provider *TargetProvider, body []byte) ([]byte, error) {
req, err := clientAdapter.DecodeEmbeddingRequest(body)
if err != nil {
e.logger.Warn("解码 Embedding 请求失败,返回原始请求", zap.String("error", err.Error()))
return body, nil
}
return providerAdapter.EncodeEmbeddingRequest(req, provider)
}
func (e *ConversionEngine) convertEmbeddingResponseBody(clientAdapter, providerAdapter ProtocolAdapter, body []byte) ([]byte, error) {
resp, err := providerAdapter.DecodeEmbeddingResponse(body)
if err != nil {
e.logger.Warn("解码 Embedding 响应失败,返回原始响应", zap.String("error", err.Error()))
return body, nil
}
return clientAdapter.EncodeEmbeddingResponse(resp)
}
func (e *ConversionEngine) convertRerankBody(clientAdapter, providerAdapter ProtocolAdapter, provider *TargetProvider, body []byte) ([]byte, error) {
req, err := clientAdapter.DecodeRerankRequest(body)
if err != nil {
e.logger.Warn("解码 Rerank 请求失败,返回原始请求", zap.String("error", err.Error()))
return body, nil
}
return providerAdapter.EncodeRerankRequest(req, provider)
}
func (e *ConversionEngine) convertRerankResponseBody(clientAdapter, providerAdapter ProtocolAdapter, body []byte) ([]byte, error) {
resp, err := providerAdapter.DecodeRerankResponse(body)
if err != nil {
return body, nil
}
return clientAdapter.EncodeRerankResponse(resp)
}
// DetectInterfaceType 检测接口类型
func (e *ConversionEngine) DetectInterfaceType(nativePath, clientProtocol string) (InterfaceType, error) {
adapter, err := e.registry.Get(clientProtocol)
if err != nil {
return InterfaceTypePassthrough, err
}
return adapter.DetectInterfaceType(nativePath), nil
}
// EncodeError 使用客户端适配器编码错误
func (e *ConversionEngine) EncodeError(err *ConversionError, clientProtocol string) ([]byte, int, error) {
adapter, adapterErr := e.registry.Get(clientProtocol)
if adapterErr != nil {
fallback := map[string]any{
"error": map[string]string{
"message": err.Error(),
"type": "internal_error",
},
}
body, _ := json.Marshal(fallback)
return body, 500, nil
}
body, statusCode := adapter.EncodeError(err)
return body, statusCode, nil
}