450 lines
14 KiB
Markdown
450 lines
14 KiB
Markdown
# 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 │
|
||
└────────┘└────────┘└────────┘└────────┘└────────┘└────────┘
|
||
```
|
||
|
||
### 详细流程设计
|
||
|
||
```go
|
||
// 完整的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. 网关层路由配置
|
||
|
||
```yaml
|
||
# 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. 网关中间件
|
||
|
||
```go
|
||
// 网关层的请求预处理中间件
|
||
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. 数据服务域作为主协调者
|
||
|
||
```go
|
||
// 数据服务域的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. 事件驱动架构
|
||
|
||
```go
|
||
// 定义领域事件
|
||
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. 异步处理
|
||
|
||
```go
|
||
// 异步计费处理
|
||
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. 缓存策略
|
||
|
||
```go
|
||
// 多级缓存策略
|
||
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. 熔断器模式
|
||
|
||
```go
|
||
// 上游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. **性能优化** - 多级缓存和异步处理
|
||
|
||
这种设计既保证了业务逻辑的清晰性,又确保了系统的高性能和高可用性。
|