tianyuan-api-server/API调用流程域设计.md
2025-07-13 20:37:12 +08:00

14 KiB
Raw Permalink Blame History

API 调用流程域设计分析

🎯 业务流程分析

根据你描述的场景,这是一个典型的B2B 数据服务 API 调用流程

用户企业 → 调用我们的产品API → 获取第三方大数据服务 → 计费扣款

🏗️ 涉及的业务域分析

这个流程总共涉及 6 个核心域

1. 🚪 网关域 (Gateway Domain)

  • 职责: 统一入口、基础路由、请求预处理
  • 功能:
    • 接收所有外部请求
    • 基础的请求验证
    • 路由到具体的业务域

2. 🛡️ 安全域 (Security Domain)

  • 职责: 认证授权、加密解密、白名单管理
  • 功能:
    • IP 白名单验证
    • 企业 ID 验证
    • 密钥管理
    • 请求参数解密

3. 👤 用户域 (User Domain)

  • 职责: 用户和企业信息管理
  • 功能:
    • 企业认证信息验证
    • 用户权限检查
    • 企业密钥获取

4. 📦 产品域 (Product Domain)

  • 职责: 产品访问控制、权限管理
  • 功能:
    • 产品访问权限验证
    • API 限额检查
    • 产品配置管理

5. 📊 数据服务域 (Data Service Domain)

  • 职责: 核心业务逻辑、第三方 API 调用
  • 功能:
    • 参数处理和转换
    • 调用上游数据公司 API
    • 数据格式转换和响应

6. 💰 计费域 (Billing Domain)

  • 职责: 计费扣款、账单管理
  • 功能:
    • 钱包余额检查
    • 费用计算
    • 扣款操作
    • 计费记录

7. 📋 审计域 (Audit Domain)

  • 职责: 请求记录、日志管理
  • 功能:
    • API 调用记录
    • 操作日志
    • 审计追踪

🔄 完整流程设计

架构图

┌─────────────┐
│   客户端     │
└─────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│                    网关域 (Gateway)                          │
│  • 请求接收  • 基础验证  • 路由分发  • 响应聚合              │
└─────────────────────────────────────────────────────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│                 数据服务域 (Data Service)                    │
│                     [主要协调者]                            │
└─────────────────────────────────────────────────────────────┘
    │      │      │      │      │      │
    ▼      ▼      ▼      ▼      ▼      ▼
┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐┌────────┐
│安全域  ││用户域  ││产品域  ││计费域  ││审计域  ││第三方  │
│Security││User    ││Product ││Billing ││Audit   ││API     │
└────────┘└────────┘└────────┘└────────┘└────────┘└────────┘

详细流程设计

// 完整的API调用流程
func (h *DataServiceHandler) ProcessAPIRequest(ctx context.Context, req *APIRequest) (*APIResponse, error) {
    // 1. 审计域 - 开始记录
    auditID := h.auditService.StartRequest(ctx, req)
    defer h.auditService.EndRequest(ctx, auditID)

    // 2. 安全域 - IP白名单验证
    if err := h.securityService.ValidateIP(ctx, req.ClientIP); err != nil {
        return nil, errors.Wrap(err, "IP not in whitelist")
    }

    // 3. 用户域 - 企业ID验证
    enterprise, err := h.userService.GetEnterpriseByID(ctx, req.EnterpriseID)
    if err != nil {
        return nil, errors.Wrap(err, "invalid enterprise ID")
    }

    // 4. 安全域 - 获取密钥并解密参数
    secretKey, err := h.securityService.GetEnterpriseSecret(ctx, req.EnterpriseID)
    if err != nil {
        return nil, errors.Wrap(err, "failed to get enterprise secret")
    }

    decryptedParams, err := h.securityService.DecryptParams(ctx, req.EncryptedParams, secretKey)
    if err != nil {
        return nil, errors.Wrap(err, "failed to decrypt parameters")
    }

    // 5. 产品域 - 产品权限验证
    if err := h.productService.ValidateAccess(ctx, enterprise.ID, req.ProductCode); err != nil {
        return nil, errors.Wrap(err, "no access to product")
    }

    // 6. 计费域 - 检查余额
    cost, err := h.billingService.CalculateCost(ctx, req.ProductCode, decryptedParams)
    if err != nil {
        return nil, errors.Wrap(err, "failed to calculate cost")
    }

    if err := h.billingService.CheckBalance(ctx, enterprise.ID, cost); err != nil {
        return nil, errors.Wrap(err, "insufficient balance")
    }

    // 7. 数据服务域 - 调用第三方API
    upstreamResp, err := h.callUpstreamAPI(ctx, decryptedParams)
    if err != nil {
        return nil, errors.Wrap(err, "upstream API call failed")
    }

    // 8. 计费域 - 扣费
    if err := h.billingService.ChargeAccount(ctx, enterprise.ID, cost, auditID); err != nil {
        // 记录扣费失败,但不影响响应
        h.logger.Error("charge failed", zap.Error(err), zap.String("audit_id", auditID))
    }

    // 9. 安全域 - 加密响应
    encryptedResp, err := h.securityService.EncryptResponse(ctx, upstreamResp, secretKey)
    if err != nil {
        return nil, errors.Wrap(err, "failed to encrypt response")
    }

    return &APIResponse{
        Data: encryptedResp,
        RequestID: auditID,
    }, nil
}

🚪 路由入口设计

1. 网关层路由配置

# gateway.yaml
routes:
    - path: /api/v1/data/*
      service: data-service
      middlewares:
          - request-id # 生成请求ID
          - rate-limit # 基础限流
          - audit-start # 开始审计
      timeout: 60s

    - path: /api/v1/admin/*
      service: admin-service
      middlewares: [admin-auth, rate-limit]

    - path: /api/v1/user/*
      service: user-service
      middlewares: [user-auth, rate-limit]

2. 网关中间件

// 网关层的请求预处理中间件
func DataAPIMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 1. 生成请求ID
        requestID := uuid.New().String()
        c.Set("request_id", requestID)
        c.Header("X-Request-ID", requestID)

        // 2. 提取企业ID从Header或路径参数
        enterpriseID := extractEnterpriseID(c)
        if enterpriseID == "" {
            c.JSON(http.StatusBadRequest, gin.H{"error": "missing enterprise ID"})
            c.Abort()
            return
        }
        c.Set("enterprise_id", enterpriseID)

        // 3. 提取客户端IP
        clientIP := c.ClientIP()
        c.Set("client_ip", clientIP)

        // 4. 提取产品代码
        productCode := c.Param("product_code")
        c.Set("product_code", productCode)

        c.Next()
    }
}

func extractEnterpriseID(c *gin.Context) string {
    // 优先从Header获取
    if id := c.GetHeader("X-Enterprise-ID"); id != "" {
        return id
    }

    // 从路径参数获取
    return c.Param("enterprise_id")
}

3. 数据服务域作为主协调者

// 数据服务域的HTTP处理器
type DataServiceHandler struct {
    securityService *SecurityService
    userService     *UserService
    productService  *ProductService
    billingService  *BillingService
    auditService    *AuditService
    upstreamClient  *UpstreamAPIClient
    logger         *zap.Logger
}

// API端点定义
func (h *DataServiceHandler) RegisterRoutes(r *gin.RouterGroup) {
    // 具体的数据产品API
    r.POST("/financial-data", h.GetFinancialData)
    r.POST("/credit-check", h.GetCreditCheck)
    r.POST("/risk-assessment", h.GetRiskAssessment)
    r.POST("/company-info", h.GetCompanyInfo)
}

// 具体业务处理器
func (h *DataServiceHandler) GetFinancialData(c *gin.Context) {
    ctx := c.Request.Context()

    // 构建请求对象
    req := &APIRequest{
        RequestID:       c.GetString("request_id"),
        EnterpriseID:    c.GetString("enterprise_id"),
        ProductCode:     "FINANCIAL_DATA",
        ClientIP:        c.GetString("client_ip"),
        EncryptedParams: c.PostForm("data"),
    }

    // 调用业务逻辑
    resp, err := h.ProcessAPIRequest(ctx, req)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{
            "error": err.Error(),
            "request_id": req.RequestID,
        })
        return
    }

    c.JSON(http.StatusOK, resp)
}

🔄 域间通信设计

1. 事件驱动架构

// 定义领域事件
type APIRequestStarted struct {
    RequestID    string    `json:"request_id"`
    EnterpriseID string    `json:"enterprise_id"`
    ProductCode  string    `json:"product_code"`
    ClientIP     string    `json:"client_ip"`
    Timestamp    time.Time `json:"timestamp"`
}

type APIRequestCompleted struct {
    RequestID    string    `json:"request_id"`
    EnterpriseID string    `json:"enterprise_id"`
    ProductCode  string    `json:"product_code"`
    Success      bool      `json:"success"`
    Cost         float64   `json:"cost"`
    Duration     int64     `json:"duration_ms"`
    Timestamp    time.Time `json:"timestamp"`
}

type ChargeRequired struct {
    RequestID    string  `json:"request_id"`
    EnterpriseID string  `json:"enterprise_id"`
    Amount       float64 `json:"amount"`
    ProductCode  string  `json:"product_code"`
}

2. 异步处理

// 异步计费处理
func (s *BillingService) HandleChargeRequired(event ChargeRequired) error {
    // 异步处理计费,避免阻塞主流程
    go func() {
        ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
        defer cancel()

        if err := s.ProcessCharge(ctx, event); err != nil {
            s.logger.Error("async charge failed",
                zap.Error(err),
                zap.String("request_id", event.RequestID))

            // 发送补偿事件
            s.eventBus.Publish(ChargeFailed{
                RequestID: event.RequestID,
                Error:     err.Error(),
            })
        }
    }()

    return nil
}

📊 性能和可扩展性考虑

1. 缓存策略

// 多级缓存策略
type CacheStrategy struct {
    // L1: 本地缓存 (企业信息、产品配置)
    localCache *cache.Cache

    // L2: Redis缓存 (密钥、白名单)
    redisCache *redis.Client

    // L3: 数据库
    db *gorm.DB
}

func (cs *CacheStrategy) GetEnterpriseSecret(enterpriseID string) (string, error) {
    // L1缓存查找
    if secret, found := cs.localCache.Get("secret:" + enterpriseID); found {
        return secret.(string), nil
    }

    // L2缓存查找
    if secret, err := cs.redisCache.Get(context.Background(), "secret:"+enterpriseID).Result(); err == nil {
        cs.localCache.Set("secret:"+enterpriseID, secret, 5*time.Minute)
        return secret, nil
    }

    // L3数据库查找
    var enterprise Enterprise
    if err := cs.db.Where("id = ?", enterpriseID).First(&enterprise).Error; err != nil {
        return "", err
    }

    // 写入缓存
    cs.redisCache.Set(context.Background(), "secret:"+enterpriseID, enterprise.SecretKey, time.Hour)
    cs.localCache.Set("secret:"+enterpriseID, enterprise.SecretKey, 5*time.Minute)

    return enterprise.SecretKey, nil
}

2. 熔断器模式

// 上游API调用熔断器
func (s *DataService) callUpstreamAPI(ctx context.Context, params map[string]interface{}) (*UpstreamResponse, error) {
    return s.circuitBreaker.Execute(func() (interface{}, error) {
        client := &http.Client{Timeout: 30 * time.Second}

        // 构建请求
        reqBody, _ := json.Marshal(params)
        req, _ := http.NewRequestWithContext(ctx, "POST", s.upstreamURL, bytes.NewBuffer(reqBody))
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set("Authorization", "Bearer "+s.upstreamToken)

        // 发送请求
        resp, err := client.Do(req)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()

        // 解析响应
        var upstreamResp UpstreamResponse
        if err := json.NewDecoder(resp.Body).Decode(&upstreamResp); err != nil {
            return nil, err
        }

        return &upstreamResp, nil
    })
}

🎯 总结

涉及的域数量:7 个域

  1. 网关域 - 统一入口
  2. 安全域 - 认证加密
  3. 用户域 - 企业验证
  4. 产品域 - 权限控制
  5. 数据服务域 - 核心业务
  6. 计费域 - 扣费计算
  7. 审计域 - 请求记录

路由入口设计:

  • 主入口: 网关域 (/api/v1/data/)
  • 业务协调: 数据服务域作为主要协调者
  • 域间通信: 通过 gRPC 调用和事件总线

设计优势:

  1. 清晰的职责分离 - 每个域专注自己的业务
  2. 高可扩展性 - 可以独立扩展任何一个域
  3. 易于测试 - 每个域可以独立测试
  4. 容错性强 - 单个域故障不影响整体
  5. 性能优化 - 多级缓存和异步处理

这种设计既保证了业务逻辑的清晰性,又确保了系统的高性能和高可用性。