14 KiB
14 KiB
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 个域
- 网关域 - 统一入口
- 安全域 - 认证加密
- 用户域 - 企业验证
- 产品域 - 权限控制
- 数据服务域 - 核心业务
- 计费域 - 扣费计算
- 审计域 - 请求记录
路由入口设计:
- 主入口: 网关域 (
/api/v1/data/
) - 业务协调: 数据服务域作为主要协调者
- 域间通信: 通过 gRPC 调用和事件总线
设计优势:
- 清晰的职责分离 - 每个域专注自己的业务
- 高可扩展性 - 可以独立扩展任何一个域
- 易于测试 - 每个域可以独立测试
- 容错性强 - 单个域故障不影响整体
- 性能优化 - 多级缓存和异步处理
这种设计既保证了业务逻辑的清晰性,又确保了系统的高性能和高可用性。