This commit is contained in:
2025-07-13 20:37:12 +08:00
parent 418b89c1ae
commit c81208e3c8
17 changed files with 6603 additions and 93 deletions

449
API调用流程域设计.md Normal file
View File

@@ -0,0 +1,449 @@
# 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. **性能优化** - 多级缓存和异步处理
这种设计既保证了业务逻辑的清晰性,又确保了系统的高性能和高可用性。

398
Domain域详解.md Normal file
View File

@@ -0,0 +1,398 @@
# Domain 域概念详解
## 🤔 什么是 Domain
**Domain** 就像现实世界中的**部门**或**业务范围**。
### 📚 现实世界的类比
想象一个大公司:
```
阿里巴巴集团
├── 电商域 (淘宝、天猫)
├── 支付域 (支付宝)
├── 云计算域 (阿里云)
├── 物流域 (菜鸟)
└── 金融域 (蚂蚁金服)
```
每个域都有:
- **专门的团队** - 不同的开发团队
- **独立的业务** - 各自负责不同的业务功能
- **清晰的边界** - 知道自己管什么,不管什么
- **独立运作** - 可以独立决策和发展
## 🏗️ 在软件架构中的应用
### 传统方式 vs 域驱动方式
#### ❌ 传统方式(技术驱动)
```
项目结构:
├── controllers/ # 所有控制器
├── services/ # 所有服务
├── models/ # 所有数据模型
└── utils/ # 工具类
问题:
- 用户相关、产品相关、支付相关的代码混在一起
- 不同业务逻辑耦合
- 团队协作困难
- 修改一个功能可能影响其他功能
```
#### ✅ 域驱动方式(业务驱动)
```
项目结构:
├── user-domain/ # 用户域
│ ├── user/ # 用户管理
│ ├── auth/ # 认证授权
│ └── profile/ # 用户资料
├── product-domain/ # 产品域
│ ├── catalog/ # 产品目录
│ ├── inventory/ # 库存管理
│ └── pricing/ # 价格管理
├── payment-domain/ # 支付域
│ ├── wallet/ # 钱包
│ ├── order/ # 订单
│ └── billing/ # 计费
└── notification-domain/ # 通知域
├── email/ # 邮件通知
├── sms/ # 短信通知
└── push/ # 推送通知
优势:
- 按业务功能组织代码
- 团队可以独立开发不同的域
- 修改用户功能不会影响支付功能
- 新人容易理解业务边界
```
## 🎯 你的项目中的 Domain 重构
### 当前问题
你的项目中有这些编码式命名:
```
IVYZ - 不知道是什么业务
FLXG - 不知道是什么业务
QYGL - 不知道是什么业务
YYSY - 不知道是什么业务
JRZQ - 不知道是什么业务
```
### 重构后的 Domain 设计
```
用户域 (User Domain)
├── 用户注册登录
├── 用户信息管理
├── 企业认证
└── 权限管理
金融域 (Financial Domain)
├── 钱包管理
├── 支付处理
├── 账单生成
└── 财务报表
数据服务域 (Data Service Domain)
├── 风险评估服务 (原 FLXG)
├── 征信查询服务 (原 JRZQ)
├── 企业信息服务 (原 QYGL)
├── 数据查询服务 (原 IVYZ)
└── 应用系统服务 (原 YYSY)
产品域 (Product Domain)
├── 产品目录管理
├── 产品订阅
├── 访问控制
└── 白名单管理
平台域 (Platform Domain)
├── 管理后台
├── 系统监控
├── 日志管理
└── 配置管理
```
## 💻 代码层面的 Domain 实现
### 1. Domain 层的职责
```go
// domain/user/entity/user.go
package entity
import (
"errors"
"time"
)
// User 用户实体
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Phone string `json:"phone"`
Status UserStatus `json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type UserStatus int
const (
UserStatusActive UserStatus = 1
UserStatusInactive UserStatus = 2
UserStatusBanned UserStatus = 3
)
// 业务规则用户名必须是3-20个字符
func (u *User) ValidateUsername() error {
if len(u.Username) < 3 || len(u.Username) > 20 {
return errors.New("用户名必须是3-20个字符")
}
return nil
}
// 业务规则:检查用户是否可以登录
func (u *User) CanLogin() bool {
return u.Status == UserStatusActive
}
// 业务规则:激活用户
func (u *User) Activate() error {
if u.Status == UserStatusBanned {
return errors.New("被封禁的用户不能激活")
}
u.Status = UserStatusActive
u.UpdatedAt = time.Now()
return nil
}
```
### 2. Domain Service (领域服务)
```go
// domain/user/service/user_service.go
package service
import (
"context"
"errors"
"your-project/domain/user/entity"
"your-project/domain/user/repository"
)
// UserDomainService 用户领域服务
type UserDomainService struct {
userRepo repository.UserRepository
}
func NewUserDomainService(userRepo repository.UserRepository) *UserDomainService {
return &UserDomainService{
userRepo: userRepo,
}
}
// 业务规则:检查用户名是否唯一
func (s *UserDomainService) IsUsernameUnique(ctx context.Context, username string) (bool, error) {
existingUser, err := s.userRepo.FindByUsername(ctx, username)
if err != nil {
return false, err
}
return existingUser == nil, nil
}
// 业务规则:用户注册
func (s *UserDomainService) RegisterUser(ctx context.Context, username, email, phone string) (*entity.User, error) {
// 1. 检查用户名唯一性
unique, err := s.IsUsernameUnique(ctx, username)
if err != nil {
return nil, err
}
if !unique {
return nil, errors.New("用户名已存在")
}
// 2. 创建用户实体
user := &entity.User{
Username: username,
Email: email,
Phone: phone,
Status: entity.UserStatusActive,
}
// 3. 验证业务规则
if err := user.ValidateUsername(); err != nil {
return nil, err
}
// 4. 保存用户
return s.userRepo.Save(ctx, user)
}
```
### 3. Repository 接口 (仓储模式)
```go
// domain/user/repository/user_repository.go
package repository
import (
"context"
"your-project/domain/user/entity"
)
// UserRepository 用户仓储接口
type UserRepository interface {
Save(ctx context.Context, user *entity.User) (*entity.User, error)
FindByID(ctx context.Context, id int64) (*entity.User, error)
FindByUsername(ctx context.Context, username string) (*entity.User, error)
FindByEmail(ctx context.Context, email string) (*entity.User, error)
Update(ctx context.Context, user *entity.User) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, offset, limit int) ([]*entity.User, error)
}
```
### 4. 应用层 (Application Layer)
```go
// application/user/service/user_app_service.go
package service
import (
"context"
userDomain "your-project/domain/user/service"
"your-project/application/user/dto"
)
// UserAppService 用户应用服务
type UserAppService struct {
userDomainService *userDomain.UserDomainService
}
func NewUserAppService(userDomainService *userDomain.UserDomainService) *UserAppService {
return &UserAppService{
userDomainService: userDomainService,
}
}
// 用户注册用例
func (s *UserAppService) RegisterUser(ctx context.Context, req *dto.RegisterUserRequest) (*dto.UserResponse, error) {
// 1. 调用领域服务
user, err := s.userDomainService.RegisterUser(ctx, req.Username, req.Email, req.Phone)
if err != nil {
return nil, err
}
// 2. 转换为 DTO
return &dto.UserResponse{
ID: user.ID,
Username: user.Username,
Email: user.Email,
Phone: user.Phone,
Status: string(user.Status),
}, nil
}
```
## 🔄 域与域之间的交互
### 事件驱动交互
```go
// 用户注册成功后,通知其他域
type UserRegisteredEvent struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
Email string `json:"email"`
}
// 金融域监听用户注册事件,自动创建钱包
func (h *WalletEventHandler) HandleUserRegistered(event UserRegisteredEvent) error {
// 为新用户创建钱包
wallet := &entity.Wallet{
UserID: event.UserID,
Balance: 0.0,
Currency: "CNY",
}
return h.walletRepo.Save(context.Background(), wallet)
}
```
## 📊 Domain 的好处
### 1. **团队协作**
```
用户域团队:专注用户相关功能
├── 张三:负责用户注册登录
├── 李四:负责用户资料管理
└── 王五:负责企业认证
支付域团队:专注支付相关功能
├── 赵六:负责钱包功能
├── 孙七:负责支付流程
└── 周八:负责账单生成
```
### 2. **独立部署**
```bash
# 可以独立部署不同的域
kubectl apply -f user-domain-deployment.yaml
kubectl apply -f payment-domain-deployment.yaml
kubectl apply -f product-domain-deployment.yaml
```
### 3. **技术选择自由**
```
用户域:使用 PostgreSQL (复杂查询)
支付域:使用 MySQL (事务性强)
数据域:使用 ClickHouse (分析查询)
```
### 4. **测试隔离**
```go
// 只测试用户域,不需要启动其他域
func TestUserDomain(t *testing.T) {
// 只需要用户域的依赖
userRepo := mock.NewUserRepository()
userService := service.NewUserDomainService(userRepo)
// 测试用户注册逻辑
user, err := userService.RegisterUser(ctx, "testuser", "test@example.com", "13800138000")
assert.NoError(t, err)
assert.Equal(t, "testuser", user.Username)
}
```
## 🎯 总结
**Domain就是按业务功能划分的代码组织方式**
1. **用户域** - 管理用户相关的所有功能
2. **支付域** - 管理支付相关的所有功能
3. **产品域** - 管理产品相关的所有功能
4. **数据域** - 管理数据查询相关的功能
**核心思想**
- 按业务划分,不按技术划分
- 每个域独立开发、测试、部署
- 域之间通过事件或 API 通信
- 团队可以专注于特定业务领域
这样组织代码后,你的项目会更容易理解、维护和扩展!

View File

@@ -2,10 +2,15 @@ package IVYZ
import (
"context"
"encoding/hex"
"tianyuan-api/pkg/crypto"
"tianyuan-api/pkg/errs"
"tianyuan-api/apps/api/internal/common"
"tianyuan-api/apps/api/internal/svc"
"tianyuan-api/apps/api/internal/types"
"tianyuan-api/apps/api/internal/validator"
"tianyuan-api/apps/api/internal/westmodel"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -25,89 +30,81 @@ func NewIVYZ0B03Logic(ctx context.Context, svcCtx *svc.ServiceContext) *IVYZ0B03
}
func (l *IVYZ0B03Logic) IVYZ0B03(req *types.Request) (resp string, err *errs.AppError) {
return
//var status string
//var charges bool
//var remark = ""
//secretKey, ok := l.ctx.Value("secretKey").(string)
//if !ok {
// return "", errs.ErrSystem
//}
//transactionID, ok := l.ctx.Value("transactionID").(string)
//if !ok {
// return "", errs.ErrSystem
//}
//userId, userIdOk := l.ctx.Value("userId").(int64)
//if !userIdOk {
// return "", errs.ErrSystem
//}
//productCode, productCodeOk := l.ctx.Value("productCode").(string)
//if !productCodeOk || productCode == "" {
// return "", errs.ErrSystem
//}
//defer func() {
// if err != nil {
// status = "failed"
// charges = false
// } else {
// status = "success"
// charges = true
// }
// sendApiRequestMessageErr := l.svcCtx.ApiRequestMqsService.SendApiRequestMessage(l.ctx, transactionID, userId, productCode, status, charges, remark)
// if sendApiRequestMessageErr != nil {
// logx.Errorf("发送 API 请求消息失败: %v", err)
// }
//}()
//// 1、解密
//key, decodeErr := hex.DecodeString(secretKey)
//if decodeErr != nil {
// return "", errs.ErrSystem
//}
//decryptData, aesDecryptErr := crypto.AesDecrypt(req.Data, key)
//if aesDecryptErr != nil || len(decryptData) == 0 {
// return "", errs.ErrParamDecryption
//}
//
//// 2、校验
//var data validator.FLXGDEC7Request
//if validatorErr := validator.ValidateAndParse(decryptData, &data); validatorErr != nil {
// return "", errs.ErrParamValidation
//}
//
//// 3、西部加密
//westConfig := l.svcCtx.Config.WestConfig
//encryptedFields, encryptStructFieldsErr := common.EncryptStructFields(data, westConfig.Key)
//if encryptStructFieldsErr != nil {
// logx.Errorf("西部加密错误:%v", encryptStructFieldsErr)
// return "", errs.ErrSystem
//}
//
//// 4、发送请求到西部
//logx.Infof("交易号:%s", transactionID)
//apiRequest := common.MapStructToAPIRequest(encryptedFields, westmodel.FLXGDEC7FieldMapping, "data")
//
//westResp, callAPIErr := l.svcCtx.WestDexService.CallAPI("G23BJ03", apiRequest, l.svcCtx.Config.WestConfig.SecretId)
//if callAPIErr != nil {
// return "", errs.ErrSystem
//}
//
//// 5、响应解析
////var respData westmodel.G32BJ05Response
////unmarshalErr := json.Unmarshal(westResp, &respData)
////if unmarshalErr != nil {
//// return "", errs.ErrSystem
////}
////
////if respData.Data.Code == "00" || respData.Data.Code == "100002" {
//// l.ctx = context.WithValue(l.ctx, "Charges", true)
////} else {
//// return "", errs.ErrSystem
////}
////encryptData, aesEncrypt := crypto.AesEncrypt(westResp, key)
////if aesEncrypt != nil {
//// return "", errs.ErrSystem
////}
//return &types.Response{
// Data: string(westResp),
//}, nil
}
var status string
var charges bool
var remark = ""
secretKey, ok := l.ctx.Value("secretKey").(string)
if !ok {
return "", errs.ErrSystem
}
transactionID, ok := l.ctx.Value("transactionID").(string)
if !ok {
return "", errs.ErrSystem
}
userId, userIdOk := l.ctx.Value("userId").(int64)
if !userIdOk {
return "", errs.ErrSystem
}
productCode, productCodeOk := l.ctx.Value("productCode").(string)
if !productCodeOk || productCode == "" {
return "", errs.ErrSystem
}
defer func() {
if err != nil {
status = "failed"
charges = false
} else {
status = "success"
charges = true
}
sendApiRequestMessageErr := l.svcCtx.ApiRequestMqsService.SendApiRequestMessage(l.ctx, transactionID, userId, productCode, status, charges, remark)
if sendApiRequestMessageErr != nil {
logx.Errorf("发送 API 请求消息失败: %v", err)
}
}()
// 1、解密
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", errs.ErrSystem
}
decryptData, aesDecryptErr := crypto.AesDecrypt(req.Data, key)
if aesDecryptErr != nil || len(decryptData) == 0 {
return "", errs.ErrParamDecryption
}
// 2、校验
var data validator.IVYZ0b03Request
if validatorErr := validator.ValidateAndParse(decryptData, &data); validatorErr != nil {
return "", errs.ErrParamValidation
}
// 3、西部加密
westConfig := l.svcCtx.Config.WestConfig
encryptedFields, encryptStructFieldsErr := common.EncryptStructFields(data, westConfig.Key)
if encryptStructFieldsErr != nil {
logx.Errorf("西部加密错误:%v", encryptStructFieldsErr)
return "", errs.ErrSystem
}
// 4、发送请求到西部
logx.Infof("交易号:%s", transactionID)
apiRequest := common.MapStructToAPIRequest(encryptedFields, westmodel.IVYZ0b03FieldMapping, "data")
westResp, callAPIErr := l.svcCtx.WestDexService.CallAPI("G17BJ02", apiRequest, l.svcCtx.Config.WestConfig.SecretId)
if callAPIErr != nil {
if callAPIErr.Code == errs.ErrDataSource.Code {
encryptData, aesEncrypt := crypto.AesEncrypt(westResp, key)
if aesEncrypt != nil {
return "", errs.ErrSystem
}
return encryptData, callAPIErr
}
return "", callAPIErr
}
encryptData, aesEncrypt := crypto.AesEncrypt(westResp, key)
if aesEncrypt != nil {
return "", errs.ErrSystem
}
return encryptData, nil
}

View File

@@ -3,11 +3,13 @@ package YYSY
import (
"context"
"encoding/hex"
"fmt"
"tianyuan-api/apps/api/internal/common"
"tianyuan-api/apps/api/internal/validator"
"tianyuan-api/apps/api/internal/westmodel"
"tianyuan-api/pkg/crypto"
"tianyuan-api/pkg/errs"
"time"
"tianyuan-api/apps/api/internal/svc"
"tianyuan-api/apps/api/internal/types"
@@ -88,9 +90,10 @@ func (l *YYSYBE08Logic) YYSYBE08(req *types.Request) (resp string, err *errs.App
// 4、发送请求到西部
logx.Infof("交易号:%s", transactionID)
apiRequest := common.MapStructToAPIRequest(encryptedFields, westmodel.YYSYBE08FieldMapping, "data")
westResp, callAPIErr := l.svcCtx.WestDexService.CallAPI("G17BJ02", apiRequest, l.svcCtx.Config.WestConfig.SecretId)
apiRequest := common.MapStructToAPIRequest(encryptedFields, westmodel.YYSYBE08FieldMapping, "")
apiRequest["customerNumber"] = l.svcCtx.Config.WestConfig.SecretId
apiRequest["timeStamp"] = fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond))
westResp, callAPIErr := l.svcCtx.WestDexService.CallAPI("layoutIdcard", apiRequest, l.svcCtx.Config.WestConfig.SecretId)
if callAPIErr != nil {
if callAPIErr.Code == errs.ErrDataSource.Code {
encryptData, aesEncrypt := crypto.AesEncrypt(westResp, key)

View File

@@ -64,8 +64,6 @@ type FLXGDEC7Request struct {
Name string `json:"name" validate:"required,min=1,validName"`
}
type IVYZ0B03Request struct {
}
type IVYZ385ERequest struct {
IDCard string `json:"id_card" validate:"required,validIDCard"`
Name string `json:"name" validate:"required,min=1,validName"`
@@ -146,10 +144,14 @@ type YYSY09CDRequest struct {
IDCard string `json:"id_card" validate:"required,validIDCard"`
Name string `json:"name" validate:"required,min=1,validName"`
}
type YYSYBE08Request struct {
type IVYZ0b03Request struct {
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
Name string `json:"name" validate:"required,min=1,validName"`
}
type YYSYBE08Request struct{
Name string `json:"name" validate:"required,min=1,validName"`
IDCard string `json:"id_card" validate:"required,validIDCard"`
}
type YYSYD50FRequest struct {
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
IDCard string `json:"id_card" validate:"required,validIDCard"`

View File

@@ -135,10 +135,14 @@ var YYSY09CDFieldMapping = map[string]string{
"MobileNo": "phone",
"MobileType": "phoneType",
}
var YYSYBE08FieldMapping = map[string]string{
var IVYZ0b03FieldMapping = map[string]string{
"Name": "name",
"MobileNo": "phone",
}
var YYSYBE08FieldMapping = map[string]string{
"MobileNo": "xM",
"IDCard": "gMSFZHM",
}
var YYSYD50FFieldMapping = map[string]string{
"MobileNo": "phone",
"IDCard": "idNo",

389
cmd目录详解.md Normal file
View File

@@ -0,0 +1,389 @@
# Go 项目中的 cmd 目录详解
## 🎯 cmd 目录的作用
`cmd` 目录是 Go 项目的标准目录布局,专门用来存放**可执行程序的入口点**。每个子目录代表一个不同的应用程序。
## 📁 目录结构示例
```
user-service/
├── cmd/ # 应用程序入口目录
│ ├── server/ # HTTP/gRPC 服务器
│ │ └── main.go # 服务器启动入口
│ ├── cli/ # 命令行工具
│ │ └── main.go # CLI 工具入口
│ ├── worker/ # 后台任务处理器
│ │ └── main.go # Worker 入口
│ └── migrator/ # 数据库迁移工具
│ └── main.go # 迁移工具入口
├── internal/ # 内部业务逻辑
├── api/ # API 定义
└── pkg/ # 可复用的包
```
## 🔧 具体示例
### 1. 服务器入口 (cmd/server/main.go)
```go
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"
"google.golang.org/grpc"
"user-service/internal/config"
"user-service/internal/server"
"user-service/internal/service"
)
func main() {
// 1. 解析命令行参数
var (
configFile = flag.String("config", "configs/config.yaml", "配置文件路径")
port = flag.Int("port", 8080, "服务端口")
)
flag.Parse()
// 2. 加载配置
cfg, err := config.Load(*configFile)
if err != nil {
log.Fatal("加载配置失败:", err)
}
// 3. 初始化服务
userService := service.NewUserService(cfg)
// 4. 创建 gRPC 服务器
grpcServer := grpc.NewServer()
server.RegisterUserServer(grpcServer, userService)
// 5. 启动服务器
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatal("监听端口失败:", err)
}
// 6. 优雅关闭
go func() {
log.Printf("用户服务启动在端口 %d", *port)
if err := grpcServer.Serve(lis); err != nil {
log.Fatal("服务启动失败:", err)
}
}()
// 7. 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("正在关闭服务...")
grpcServer.GracefulStop()
log.Println("服务已关闭")
}
```
### 2. CLI 工具入口 (cmd/cli/main.go)
```go
package main
import (
"flag"
"fmt"
"os"
"user-service/internal/config"
"user-service/internal/service"
)
func main() {
var (
action = flag.String("action", "", "执行的动作: create-user, list-users, reset-password")
userID = flag.Int64("user-id", 0, "用户ID")
username = flag.String("username", "", "用户名")
)
flag.Parse()
cfg, err := config.Load("configs/config.yaml")
if err != nil {
fmt.Printf("加载配置失败: %v\n", err)
os.Exit(1)
}
userService := service.NewUserService(cfg)
switch *action {
case "create-user":
if *username == "" {
fmt.Println("用户名不能为空")
os.Exit(1)
}
user, err := userService.CreateUser(*username)
if err != nil {
fmt.Printf("创建用户失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("用户创建成功: ID=%d, Username=%s\n", user.ID, user.Username)
case "list-users":
users, err := userService.ListUsers()
if err != nil {
fmt.Printf("获取用户列表失败: %v\n", err)
os.Exit(1)
}
for _, user := range users {
fmt.Printf("ID: %d, Username: %s, Email: %s\n", user.ID, user.Username, user.Email)
}
case "reset-password":
if *userID == 0 {
fmt.Println("用户ID不能为空")
os.Exit(1)
}
newPassword, err := userService.ResetPassword(*userID)
if err != nil {
fmt.Printf("重置密码失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("密码重置成功,新密码: %s\n", newPassword)
default:
fmt.Println("不支持的操作,支持的操作: create-user, list-users, reset-password")
os.Exit(1)
}
}
```
### 3. Worker 入口 (cmd/worker/main.go)
```go
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
"user-service/internal/config"
"user-service/internal/worker"
)
func main() {
cfg, err := config.Load("configs/config.yaml")
if err != nil {
log.Fatal("加载配置失败:", err)
}
// 创建上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 初始化 Worker
emailWorker := worker.NewEmailWorker(cfg)
notificationWorker := worker.NewNotificationWorker(cfg)
// 启动 Workers
go func() {
log.Println("启动邮件处理 Worker...")
if err := emailWorker.Start(ctx); err != nil {
log.Printf("邮件 Worker 错误: %v", err)
}
}()
go func() {
log.Println("启动通知处理 Worker...")
if err := notificationWorker.Start(ctx); err != nil {
log.Printf("通知 Worker 错误: %v", err)
}
}()
// 优雅关闭
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("正在关闭 Workers...")
cancel()
// 等待 Workers 完成
time.Sleep(5 * time.Second)
log.Println("Workers 已关闭")
}
```
### 4. 数据库迁移工具 (cmd/migrator/main.go)
```go
package main
import (
"flag"
"log"
"user-service/internal/config"
"user-service/internal/migration"
)
func main() {
var (
direction = flag.String("direction", "up", "迁移方向: up 或 down")
steps = flag.Int("steps", 0, "迁移步数0表示全部")
)
flag.Parse()
cfg, err := config.Load("configs/config.yaml")
if err != nil {
log.Fatal("加载配置失败:", err)
}
migrator := migration.NewMigrator(cfg.Database.URL)
switch *direction {
case "up":
log.Println("执行数据库迁移...")
if err := migrator.Up(*steps); err != nil {
log.Fatal("迁移失败:", err)
}
log.Println("迁移成功")
case "down":
log.Println("回滚数据库迁移...")
if err := migrator.Down(*steps); err != nil {
log.Fatal("回滚失败:", err)
}
log.Println("回滚成功")
default:
log.Fatal("不支持的迁移方向:", *direction)
}
}
```
## 🎯 为什么这样设计?
### 1. **关注点分离**
- `cmd/` 只负责程序启动和配置解析
- `internal/` 负责具体的业务逻辑
- 每个应用程序有独立的入口点
### 2. **多种运行模式**
```bash
# 启动 HTTP/gRPC 服务器
./user-service-server -port=8080 -config=prod.yaml
# 使用 CLI 工具
./user-service-cli -action=create-user -username=john
# 启动后台 Worker
./user-service-worker
# 执行数据库迁移
./user-service-migrator -direction=up
```
### 3. **构建和部署灵活性**
```dockerfile
# Dockerfile 示例
FROM golang:1.20-alpine AS builder
WORKDIR /app
COPY . .
# 分别构建不同的应用程序
RUN go build -o server ./cmd/server
RUN go build -o cli ./cmd/cli
RUN go build -o worker ./cmd/worker
RUN go build -o migrator ./cmd/migrator
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# 根据需要复制不同的可执行文件
COPY --from=builder /app/server .
COPY --from=builder /app/migrator .
# 可以选择启动不同的程序
CMD ["./server"]
```
### 4. **Makefile 示例**
```makefile
# Makefile
.PHONY: build-server build-cli build-worker build-migrator
build-server:
go build -o bin/server ./cmd/server
build-cli:
go build -o bin/cli ./cmd/cli
build-worker:
go build -o bin/worker ./cmd/worker
build-migrator:
go build -o bin/migrator ./cmd/migrator
build-all: build-server build-cli build-worker build-migrator
run-server:
./bin/server -port=8080
run-worker:
./bin/worker
migrate-up:
./bin/migrator -direction=up
migrate-down:
./bin/migrator -direction=down -steps=1
```
## 🚀 在你的项目中的应用
在你当前的项目中,每个服务都应该有这样的结构:
```
apps/user/
├── cmd/
│ ├── server/main.go # gRPC 服务器
│ ├── cli/main.go # 用户管理 CLI
│ └── migrator/main.go # 数据库迁移
├── internal/ # 业务逻辑
├── user.proto # API 定义
└── Dockerfile
apps/gateway/
├── cmd/
│ ├── server/main.go # HTTP 网关服务器
│ └── cli/main.go # 网关管理 CLI
├── internal/
├── gateway.api
└── Dockerfile
```
## 📋 总结
`cmd` 目录的核心作用:
1. **程序入口点** - 每个 main.go 是一个独立的应用程序
2. **配置解析** - 处理命令行参数和配置文件
3. **依赖注入** - 初始化和连接各个组件
4. **生命周期管理** - 启动、运行、优雅关闭
5. **多种运行模式** - 服务器、CLI、Worker 等不同形态

View File

@@ -0,0 +1,744 @@
# go-zero 服务实现详解
## 🔥 核心服务实现
### 1. Gateway API 服务 (HTTP 入口)
#### API 定义 (gateway.api)
```go
syntax = "v1"
info(
title: "天远API网关"
desc: "统一API入口"
version: "v1.0"
)
type (
// 登录请求
LoginReq {
Username string `json:"username"`
Password string `json:"password"`
}
LoginResp {
Token string `json:"token"`
UserInfo UserInfo `json:"userInfo"`
}
UserInfo {
UserId int64 `json:"userId"`
Username string `json:"username"`
EnterpriseId int64 `json:"enterpriseId"`
}
// 数据查询请求 (你的核心业务)
DataQueryReq {
QueryType string `json:"queryType"` // risk/credit/company/data
Parameters map[string]interface{} `json:"parameters"`
}
DataQueryResp {
Success bool `json:"success"`
Data interface{} `json:"data"`
TransactionId string `json:"transactionId"`
RemainingBalance float64 `json:"remainingBalance"`
}
)
@server(
jwt: Auth
group: auth
)
service gateway-api {
@handler LoginHandler
post /api/v1/auth/login (LoginReq) returns (LoginResp)
@handler LogoutHandler
post /api/v1/auth/logout returns ()
}
@server(
jwt: Auth
group: data
middleware: RateLimit,Audit
)
service gateway-api {
@handler RiskAssessmentHandler
post /api/v1/data/risk-assessment (DataQueryReq) returns (DataQueryResp)
@handler CreditCheckHandler
post /api/v1/data/credit-check (DataQueryReq) returns (DataQueryResp)
@handler CompanyInfoHandler
post /api/v1/data/company-info (DataQueryReq) returns (DataQueryResp)
@handler DataQueryHandler
post /api/v1/data/query (DataQueryReq) returns (DataQueryResp)
}
```
#### 核心 Logic 实现 (处理复杂调用链)
```go
// api/gateway/internal/logic/data/risklogic.go
type RiskLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func (l *RiskLogic) RiskAssessment(req *types.DataQueryReq) (resp *types.DataQueryResp, err error) {
// 获取用户信息 (从JWT中解析)
userId := ctxdata.GetUidFromCtx(l.ctx)
// 🔥 调用数据域RPC进行复杂业务处理
dataResp, err := l.svcCtx.DataRpc.ProcessDataRequest(l.ctx, &data.ProcessDataRequestReq{
UserId: userId,
QueryType: "risk-assessment",
Parameters: req.Parameters,
ClientIp: httpx.GetClientIP(l.ctx),
})
if err != nil {
logx.Errorf("调用数据域RPC失败: %v", err)
return nil, err
}
return &types.DataQueryResp{
Success: dataResp.Success,
Data: dataResp.Data,
TransactionId: dataResp.TransactionId,
RemainingBalance: dataResp.RemainingBalance,
}, nil
}
```
#### 服务上下文 (包含所有 RPC 客户端)
```go
// api/gateway/internal/svc/servicecontext.go
type ServiceContext struct {
Config config.Config
// 🔗 RPC客户端连接
UserRpc user.User
DataRpc data.Data
SecurityRpc security.Security
BillingRpc billing.Billing
ProductRpc product.Product
AuditRpc audit.Audit
// 中间件
RateLimit rest.Middleware
AuditMiddleware rest.Middleware
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
// 初始化RPC客户端
UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)),
DataRpc: data.NewData(zrpc.MustNewClient(c.DataRpc)),
SecurityRpc: security.NewSecurity(zrpc.MustNewClient(c.SecurityRpc)),
BillingRpc: billing.NewBilling(zrpc.MustNewClient(c.BillingRpc)),
ProductRpc: product.NewProduct(zrpc.MustNewClient(c.ProductRpc)),
AuditRpc: audit.NewAudit(zrpc.MustNewClient(c.AuditRpc)),
// 初始化中间件
RateLimit: ratelimit.NewRateLimit(c.RateLimit),
AuditMiddleware: auditrpc.NewAuditMiddleware(c.Audit),
}
}
```
### 2. Data RPC 服务 (核心协调者)
#### Proto 定义 (data.proto)
```protobuf
syntax = "proto3";
package data;
option go_package = "./pb";
// 数据处理请求
message ProcessDataRequestReq {
int64 user_id = 1;
string query_type = 2; // risk-assessment/credit-check/company-info/data-query
map<string, string> parameters = 3;
string client_ip = 4;
}
message ProcessDataRequestResp {
bool success = 1;
string data = 2; // JSON格式的业务数据
string transaction_id = 3;
double remaining_balance = 4;
string error_message = 5;
}
service Data {
rpc ProcessDataRequest(ProcessDataRequestReq) returns (ProcessDataRequestResp);
}
```
#### 核心协调逻辑 (你的复杂业务流程)
```go
// rpc/data/internal/logic/orchestrator/dataorchestratorlogic.go
type DataOrchestratorLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func (l *DataOrchestratorLogic) ProcessDataRequest(in *pb.ProcessDataRequestReq) (*pb.ProcessDataRequestResp, error) {
startTime := time.Now()
// === 第1步安全验证 ===
// 1.1 获取用户企业信息
userResp, err := l.svcCtx.UserRpc.GetUserInfo(l.ctx, &user.GetUserInfoReq{
UserId: in.UserId,
})
if err != nil {
return nil, fmt.Errorf("获取用户信息失败: %w", err)
}
// 1.2 IP白名单验证
_, err = l.svcCtx.SecurityRpc.CheckWhitelist(l.ctx, &security.CheckWhitelistReq{
EnterpriseId: userResp.EnterpriseId,
ClientIp: in.ClientIp,
})
if err != nil {
return nil, fmt.Errorf("IP白名单验证失败: %w", err)
}
// 1.3 密钥解密
decryptResp, err := l.svcCtx.SecurityRpc.DecryptSecret(l.ctx, &security.DecryptSecretReq{
EnterpriseId: userResp.EnterpriseId,
EncryptedKey: userResp.EncryptedSecretKey,
})
if err != nil {
return nil, fmt.Errorf("密钥解密失败: %w", err)
}
// === 第2步权限与产品验证 ===
// 2.1 产品权限检查
productResp, err := l.svcCtx.ProductRpc.CheckProductAccess(l.ctx, &product.CheckProductAccessReq{
UserId: in.UserId,
QueryType: in.QueryType,
SecretKey: decryptResp.SecretKey,
})
if err != nil {
return nil, fmt.Errorf("产品权限检查失败: %w", err)
}
// 2.2 余额检查
balanceResp, err := l.svcCtx.BillingRpc.CheckBalance(l.ctx, &billing.CheckBalanceReq{
UserId: in.UserId,
ProductCode: productResp.ProductCode,
QueryType: in.QueryType,
})
if err != nil {
return nil, fmt.Errorf("余额不足: %w", err)
}
// === 第3步执行业务逻辑 ===
var businessResult *BusinessResult
switch in.QueryType {
case "risk-assessment":
businessResult, err = l.processRiskAssessment(in.Parameters)
case "credit-check":
businessResult, err = l.processCreditCheck(in.Parameters)
case "company-info":
businessResult, err = l.processCompanyInfo(in.Parameters)
case "data-query":
businessResult, err = l.processDataQuery(in.Parameters)
default:
return nil, errors.New("不支持的查询类型")
}
if err != nil {
return nil, fmt.Errorf("业务处理失败: %w", err)
}
// === 第4步计费和审计 ===
// 4.1 执行扣费
chargeResp, err := l.svcCtx.BillingRpc.Charge(l.ctx, &billing.ChargeReq{
UserId: in.UserId,
ProductCode: productResp.ProductCode,
Amount: balanceResp.RequiredAmount,
TransactionType: in.QueryType,
RequestId: generateRequestId(),
})
if err != nil {
return nil, fmt.Errorf("扣费失败: %w", err)
}
// 4.2 异步记录审计日志
go func() {
l.svcCtx.AuditRpc.RecordAPICall(context.Background(), &audit.RecordAPICallReq{
UserId: in.UserId,
EnterpriseId: userResp.EnterpriseId,
QueryType: in.QueryType,
ClientIp: in.ClientIp,
TransactionId: chargeResp.TransactionId,
ResponseTime: time.Since(startTime).Milliseconds(),
Status: "success",
})
}()
return &pb.ProcessDataRequestResp{
Success: true,
Data: businessResult.ToJSON(),
TransactionId: chargeResp.TransactionId,
RemainingBalance: chargeResp.RemainingBalance,
}, nil
}
// 🔥 原FLXG逻辑 - 风险评估
func (l *DataOrchestratorLogic) processRiskAssessment(params map[string]string) (*BusinessResult, error) {
// 调用西部数据源
westData, err := l.svcCtx.WestDexClient.QueryRiskData(params)
if err != nil {
return nil, err
}
// 调用百度风控API
baiduData, err := l.svcCtx.BaiduClient.RiskAssessment(params)
if err != nil {
logx.Errorf("百度API调用失败: %v", err)
// 降级处理,只使用西部数据
}
// 数据融合处理
result := &BusinessResult{
Code: "FLXG001",
Data: mergeRiskData(westData, baiduData),
Source: "west+baidu",
}
return result, nil
}
// 🔥 原JRZQ逻辑 - 征信查询
func (l *DataOrchestratorLogic) processCreditCheck(params map[string]string) (*BusinessResult, error) {
// 调用征信API
creditData, err := l.svcCtx.CreditClient.QueryCredit(params)
if err != nil {
return nil, err
}
return &BusinessResult{
Code: "JRZQ001",
Data: creditData,
Source: "credit_bureau",
}, nil
}
```
### 3. 数据库操作 (go-zero Model)
#### 用户模型
```go
// rpc/user/internal/model/usermodel.go
type User struct {
Id int64 `db:"id"`
Username string `db:"username"`
Password string `db:"password"`
Email string `db:"email"`
EnterpriseId int64 `db:"enterprise_id"`
Status int64 `db:"status"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
type UserModel interface {
Insert(ctx context.Context, data *User) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*User, error)
FindOneByUsername(ctx context.Context, username string) (*User, error)
Update(ctx context.Context, data *User) error
Delete(ctx context.Context, id int64) error
}
type defaultUserModel struct {
conn sqlx.SqlConn
table string
}
func NewUserModel(conn sqlx.SqlConn) UserModel {
return &defaultUserModel{
conn: conn,
table: "`users`",
}
}
func (m *defaultUserModel) FindOneByUsername(ctx context.Context, username string) (*User, error) {
query := fmt.Sprintf("select %s from %s where `username` = ? limit 1", userRows, m.table)
var resp User
err := m.conn.QueryRowCtx(ctx, &resp, query, username)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
```
#### 缓存处理
```go
// rpc/user/internal/logic/user/getuserinfologic.go
func (l *GetUserInfoLogic) GetUserInfo(in *pb.GetUserInfoReq) (*pb.GetUserInfoResp, error) {
// 1. 先查缓存
cacheKey := fmt.Sprintf("user:info:%d", in.UserId)
cached, err := l.svcCtx.RedisClient.Get(cacheKey)
if err == nil && cached != "" {
var userInfo pb.GetUserInfoResp
if json.Unmarshal([]byte(cached), &userInfo) == nil {
return &userInfo, nil
}
}
// 2. 查数据库
user, err := l.svcCtx.UserModel.FindOne(l.ctx, in.UserId)
if err != nil {
return nil, err
}
enterprise, err := l.svcCtx.EnterpriseModel.FindOne(l.ctx, user.EnterpriseId)
if err != nil {
return nil, err
}
resp := &pb.GetUserInfoResp{
UserId: user.Id,
Username: user.Username,
Email: user.Email,
EnterpriseId: user.EnterpriseId,
EnterpriseName: enterprise.Name,
EncryptedSecretKey: enterprise.EncryptedSecretKey,
}
// 3. 写入缓存 (5分钟过期)
respJson, _ := json.Marshal(resp)
l.svcCtx.RedisClient.Setex(cacheKey, string(respJson), 300)
return resp, nil
}
```
## 🚀 部署配置
### Docker 部署
#### 1. 服务 Dockerfile
```dockerfile
# rpc/user/Dockerfile
FROM golang:1.19-alpine AS builder
WORKDIR /build
COPY . .
RUN go mod download
RUN go build -ldflags="-s -w" -o user rpc/user/user.go
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /build/user .
COPY --from=builder /build/rpc/user/etc/user.yaml ./etc/
EXPOSE 8001
CMD ["./user", "-f", "etc/user.yaml"]
```
#### 2. Docker Compose (开发环境)
```yaml
# deploy/docker/docker-compose.dev.yml
version: "3.8"
services:
# 基础设施
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: tianyuan
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
redis:
image: redis:7-alpine
ports:
- "6379:6379"
etcd:
image: quay.io/coreos/etcd:v3.5.0
environment:
- ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
- ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
ports:
- "2379:2379"
kafka:
image: confluentinc/cp-kafka:latest
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
ports:
- "9092:9092"
depends_on:
- zookeeper
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
# 微服务
user-rpc:
build:
context: ../../
dockerfile: rpc/user/Dockerfile
ports:
- "8001:8001"
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
- ETCD_HOSTS=etcd:2379
depends_on:
- mysql
- redis
- etcd
data-rpc:
build:
context: ../../
dockerfile: rpc/data/Dockerfile
ports:
- "8002:8002"
environment:
- DB_HOST=mysql
- REDIS_HOST=redis
- ETCD_HOSTS=etcd:2379
- USER_RPC=user-rpc:8001
depends_on:
- user-rpc
gateway-api:
build:
context: ../../
dockerfile: api/gateway/Dockerfile
ports:
- "8000:8000"
environment:
- USER_RPC=user-rpc:8001
- DATA_RPC=data-rpc:8002
- SECURITY_RPC=security-rpc:8003
- BILLING_RPC=billing-rpc:8004
depends_on:
- user-rpc
- data-rpc
volumes:
mysql_data:
```
#### 3. Kubernetes 部署
```yaml
# deploy/k8s/user-rpc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-rpc
spec:
replicas: 3
selector:
matchLabels:
app: user-rpc
template:
metadata:
labels:
app: user-rpc
spec:
containers:
- name: user-rpc
image: tianyuan/user-rpc:latest
ports:
- containerPort: 8001
env:
- name: DB_HOST
value: "mysql-svc"
- name: REDIS_HOST
value: "redis-svc"
- name: ETCD_HOSTS
value: "etcd-svc:2379"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8001
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8001
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-rpc-svc
spec:
selector:
app: user-rpc
ports:
- port: 8001
targetPort: 8001
type: ClusterIP
```
### 4. Makefile (统一构建部署)
```makefile
# Makefile
.PHONY: build-all start-dev stop-dev deploy-k8s
# 构建所有服务
build-all:
@echo "构建所有微服务..."
cd api/gateway && go build -o ../../bin/gateway gateway.go
cd rpc/user && go build -o ../../bin/user-rpc user.go
cd rpc/data && go build -o ../../bin/data-rpc data.go
cd rpc/security && go build -o ../../bin/security-rpc security.go
cd rpc/billing && go build -o ../../bin/billing-rpc billing.go
# 生成代码
gen-api:
cd api/gateway && goctl api go -api gateway.api -dir .
gen-rpc:
cd rpc/user && goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=.
# 开发环境
start-dev:
docker-compose -f deploy/docker/docker-compose.dev.yml up -d
stop-dev:
docker-compose -f deploy/docker/docker-compose.dev.yml down
# 数据库迁移
migrate-up:
cd tools/migrate && go run migrate.go up
migrate-down:
cd tools/migrate && go run migrate.go down
# K8s部署
deploy-k8s:
kubectl apply -f deploy/k8s/namespace.yaml
kubectl apply -f deploy/k8s/configmap.yaml
kubectl apply -f deploy/k8s/mysql.yaml
kubectl apply -f deploy/k8s/redis.yaml
kubectl apply -f deploy/k8s/user-rpc.yaml
kubectl apply -f deploy/k8s/data-rpc.yaml
kubectl apply -f deploy/k8s/gateway-api.yaml
# 测试
test-all:
go test ./api/gateway/...
go test ./rpc/user/...
go test ./rpc/data/...
# 代码检查
lint:
golangci-lint run ./...
# 清理
clean:
rm -rf bin/
docker system prune -f
```
## 🔄 CI/CD 配置
```yaml
# .github/workflows/deploy.yml
name: Deploy Microservices
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Build services
run: make build-all
- name: Run tests
run: make test-all
- name: Build Docker images
run: |
docker build -t tianyuan/gateway:${{ github.sha }} api/gateway/
docker build -t tianyuan/user-rpc:${{ github.sha }} rpc/user/
docker build -t tianyuan/data-rpc:${{ github.sha }} rpc/data/
- name: Push to registry
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push tianyuan/gateway:${{ github.sha }}
docker push tianyuan/user-rpc:${{ github.sha }}
docker push tianyuan/data-rpc:${{ github.sha }}
- name: Deploy to K8s
run: |
echo "${{ secrets.KUBECONFIG }}" | base64 -d > kubeconfig
export KUBECONFIG=kubeconfig
sed -i 's|latest|${{ github.sha }}|g' deploy/k8s/*.yaml
kubectl apply -f deploy/k8s/
```
这个架构设计完全基于 go-zero 框架,保持了你原有业务逻辑的同时,提供了清晰的服务边界和强大的扩展能力。每个服务都可以独立开发、测试、部署和扩容。

View File

@@ -0,0 +1,739 @@
# go-zero 错误分级与链路追踪设计
## 1. 错误分级体系
### 1.1 错误等级定义
```go
// shared/errcode/levels.go
package errcode
// 错误等级枚举
type ErrorLevel int
const (
LevelDebug ErrorLevel = iota // 调试级别:开发调试信息
LevelInfo // 信息级别:一般业务信息
LevelWarn // 警告级别:需要关注但不影响业务
LevelError // 错误级别:业务错误,需要处理
LevelFatal // 致命级别:系统级错误,影响服务
LevelPanic // 恐慌级别:严重错误,服务不可用
)
// 错误等级字符串映射
var LevelNames = map[ErrorLevel]string{
LevelDebug: "DEBUG",
LevelInfo: "INFO",
LevelWarn: "WARN",
LevelError: "ERROR",
LevelFatal: "FATAL",
LevelPanic: "PANIC",
}
func (l ErrorLevel) String() string {
if name, ok := LevelNames[l]; ok {
return name
}
return "UNKNOWN"
}
```
### 1.2 错误分类体系
```go
// shared/errcode/types.go
package errcode
import (
"fmt"
"time"
)
// 错误类型
type ErrorType string
const (
// 系统级错误
ErrorTypeSystem ErrorType = "SYSTEM" // 系统错误
ErrorTypeNetwork ErrorType = "NETWORK" // 网络错误
ErrorTypeDatabase ErrorType = "DATABASE" // 数据库错误
ErrorTypeRedis ErrorType = "REDIS" // Redis错误
ErrorTypeMQ ErrorType = "MQ" // 消息队列错误
ErrorTypeRPC ErrorType = "RPC" // RPC调用错误
// 业务级错误
ErrorTypeBusiness ErrorType = "BUSINESS" // 业务逻辑错误
ErrorTypeValidation ErrorType = "VALIDATION" // 参数校验错误
ErrorTypeAuth ErrorType = "AUTH" // 认证授权错误
ErrorTypePermission ErrorType = "PERMISSION" // 权限错误
// 客户端错误
ErrorTypeParam ErrorType = "PARAM" // 参数错误
ErrorTypeRequest ErrorType = "REQUEST" // 请求错误
ErrorTypeResponse ErrorType = "RESPONSE" // 响应错误
)
// 统一错误结构
type AppError struct {
Code string `json:"code"` // 错误码
Message string `json:"message"` // 错误消息
Level ErrorLevel `json:"level"` // 错误等级
Type ErrorType `json:"type"` // 错误类型
TraceId string `json:"trace_id"` // 链路追踪ID
SpanId string `json:"span_id"` // 跨度ID
Service string `json:"service"` // 服务名称
Method string `json:"method"` // 方法名称
Timestamp time.Time `json:"timestamp"` // 时间戳
Details interface{} `json:"details"` // 详细信息
Stack string `json:"stack"` // 堆栈信息(仅错误级别以上)
Cause error `json:"-"` // 原始错误(不序列化)
}
// 实现error接口
func (e *AppError) Error() string {
return fmt.Sprintf("[%s][%s][%s] %s: %s",
e.Level.String(), e.Type, e.Code, e.Service, e.Message)
}
// 获取原始错误
func (e *AppError) Unwrap() error {
return e.Cause
}
```
### 1.3 错误构造器
```go
// shared/errcode/builder.go
package errcode
import (
"runtime"
"time"
"github.com/zeromicro/go-zero/core/trace"
)
type ErrorBuilder struct {
service string
method string
}
func NewErrorBuilder(service, method string) *ErrorBuilder {
return &ErrorBuilder{
service: service,
method: method,
}
}
// Debug级别错误
func (b *ErrorBuilder) Debug(code, message string) *AppError {
return b.buildError(LevelDebug, ErrorTypeSystem, code, message, nil, nil)
}
// Info级别错误
func (b *ErrorBuilder) Info(code, message string) *AppError {
return b.buildError(LevelInfo, ErrorTypeSystem, code, message, nil, nil)
}
// Warn级别错误
func (b *ErrorBuilder) Warn(errorType ErrorType, code, message string) *AppError {
return b.buildError(LevelWarn, errorType, code, message, nil, nil)
}
// Error级别错误
func (b *ErrorBuilder) Error(errorType ErrorType, code, message string, cause error) *AppError {
return b.buildError(LevelError, errorType, code, message, cause, nil)
}
// Fatal级别错误
func (b *ErrorBuilder) Fatal(errorType ErrorType, code, message string, cause error) *AppError {
return b.buildError(LevelFatal, errorType, code, message, cause, nil)
}
// Panic级别错误
func (b *ErrorBuilder) Panic(errorType ErrorType, code, message string, cause error) *AppError {
return b.buildError(LevelPanic, errorType, code, message, cause, nil)
}
// 业务错误(常用)
func (b *ErrorBuilder) BusinessError(code, message string) *AppError {
return b.buildError(LevelError, ErrorTypeBusiness, code, message, nil, nil)
}
// 参数校验错误(常用)
func (b *ErrorBuilder) ValidationError(code, message string, details interface{}) *AppError {
return b.buildError(LevelWarn, ErrorTypeValidation, code, message, nil, details)
}
// 权限错误(常用)
func (b *ErrorBuilder) PermissionError(code, message string) *AppError {
return b.buildError(LevelWarn, ErrorTypePermission, code, message, nil, nil)
}
// 系统错误(常用)
func (b *ErrorBuilder) SystemError(code, message string, cause error) *AppError {
return b.buildError(LevelFatal, ErrorTypeSystem, code, message, cause, nil)
}
// 构建错误
func (b *ErrorBuilder) buildError(level ErrorLevel, errorType ErrorType, code, message string, cause error, details interface{}) *AppError {
appErr := &AppError{
Code: code,
Message: message,
Level: level,
Type: errorType,
Service: b.service,
Method: b.method,
Timestamp: time.Now(),
Details: details,
Cause: cause,
}
// 获取链路追踪信息
if traceId := trace.TraceIDFromContext(ctx); traceId != "" {
appErr.TraceId = traceId
}
if spanId := trace.SpanIDFromContext(ctx); spanId != "" {
appErr.SpanId = spanId
}
// 错误级别以上记录堆栈信息
if level >= LevelError {
appErr.Stack = getStackTrace()
}
return appErr
}
// 获取堆栈信息
func getStackTrace() string {
buf := make([]byte, 4096)
n := runtime.Stack(buf, false)
return string(buf[:n])
}
```
## 2. 链路追踪集成
### 2.1 链路追踪配置
```yaml
# etc/client-api.yaml
Name: client-api
Host: 0.0.0.0
Port: 8080
# 链路追踪配置
Telemetry:
Name: client-api
Endpoint: http://jaeger:14268/api/traces
Sampler: 1.0
Batcher: jaeger
# 日志配置
Log:
ServiceName: client-api
Mode: file
Level: info
Path: logs
MaxSize: 100
MaxAge: 7
MaxBackups: 5
Compress: true
```
### 2.2 链路追踪中间件
```go
// shared/middleware/trace_middleware.go
package middleware
import (
"context"
"net/http"
"github.com/zeromicro/go-zero/core/trace"
"github.com/zeromicro/go-zero/rest/httpx"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"tianyuan/shared/errcode"
)
// HTTP链路追踪中间件
func TraceMiddleware(serviceName string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tracer := otel.Tracer(serviceName)
// 开始span
ctx, span := tracer.Start(r.Context(), r.URL.Path)
defer span.End()
// 设置span属性
span.SetAttributes(
attribute.String("http.method", r.Method),
attribute.String("http.url", r.URL.String()),
attribute.String("http.user_agent", r.UserAgent()),
attribute.String("service.name", serviceName),
)
// 将链路信息注入上下文
r = r.WithContext(ctx)
// 创建响应包装器
wrapper := &responseWrapper{
ResponseWriter: w,
statusCode: http.StatusOK,
}
// 执行下一个处理器
next.ServeHTTP(wrapper, r)
// 设置响应属性
span.SetAttributes(
attribute.Int("http.status_code", wrapper.statusCode),
)
// 如果是错误状态码设置span状态
if wrapper.statusCode >= 400 {
span.SetStatus(codes.Error, http.StatusText(wrapper.statusCode))
}
})
}
}
// 响应包装器
type responseWrapper struct {
http.ResponseWriter
statusCode int
}
func (w *responseWrapper) WriteHeader(statusCode int) {
w.statusCode = statusCode
w.ResponseWriter.WriteHeader(statusCode)
}
```
### 2.3 RPC 链路追踪拦截器
```go
// shared/interceptor/trace_interceptor.go
package interceptor
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"tianyuan/shared/errcode"
)
// RPC客户端链路追踪拦截器
func TraceClientInterceptor(serviceName string) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{},
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
tracer := otel.Tracer(serviceName)
ctx, span := tracer.Start(ctx, method)
defer span.End()
// 设置span属性
span.SetAttributes(
attribute.String("rpc.method", method),
attribute.String("rpc.service", serviceName),
attribute.String("rpc.system", "grpc"),
)
// 调用RPC
err := invoker(ctx, method, req, reply, cc, opts...)
// 处理错误
if err != nil {
span.SetStatus(codes.Error, err.Error())
span.SetAttributes(
attribute.String("rpc.grpc.status_code", status.Code(err).String()),
)
}
return err
}
}
// RPC服务端链路追踪拦截器
func TraceServerInterceptor(serviceName string) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
tracer := otel.Tracer(serviceName)
ctx, span := tracer.Start(ctx, info.FullMethod)
defer span.End()
// 设置span属性
span.SetAttributes(
attribute.String("rpc.method", info.FullMethod),
attribute.String("rpc.service", serviceName),
attribute.String("rpc.system", "grpc"),
)
// 调用处理器
resp, err := handler(ctx, req)
// 处理错误
if err != nil {
span.SetStatus(codes.Error, err.Error())
// 如果是自定义错误,记录更多信息
if appErr, ok := err.(*errcode.AppError); ok {
span.SetAttributes(
attribute.String("error.type", string(appErr.Type)),
attribute.String("error.code", appErr.Code),
attribute.String("error.level", appErr.Level.String()),
)
}
}
return resp, err
}
}
```
## 3. 日志集成
### 3.1 结构化日志
```go
// shared/logger/logger.go
package logger
import (
"context"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/trace"
"tianyuan/shared/errcode"
)
// 日志字段
type LogFields map[string]interface{}
// 结构化日志器
type StructuredLogger struct {
service string
method string
}
func NewStructuredLogger(service, method string) *StructuredLogger {
return &StructuredLogger{
service: service,
method: method,
}
}
// 记录错误日志
func (l *StructuredLogger) LogError(ctx context.Context, err error, fields LogFields) {
logFields := l.buildBaseFields(ctx, fields)
if appErr, ok := err.(*errcode.AppError); ok {
// 自定义错误
logFields["error_code"] = appErr.Code
logFields["error_type"] = appErr.Type
logFields["error_level"] = appErr.Level.String()
logFields["error_details"] = appErr.Details
// 根据错误级别选择日志方法
switch appErr.Level {
case errcode.LevelDebug:
logx.WithContext(ctx).WithFields(logFields).Info(appErr.Message)
case errcode.LevelInfo:
logx.WithContext(ctx).WithFields(logFields).Info(appErr.Message)
case errcode.LevelWarn:
logx.WithContext(ctx).WithFields(logFields).Slow(appErr.Message)
case errcode.LevelError:
logx.WithContext(ctx).WithFields(logFields).Error(appErr.Message)
case errcode.LevelFatal, errcode.LevelPanic:
logx.WithContext(ctx).WithFields(logFields).Severe(appErr.Message)
}
} else {
// 普通错误
logFields["error"] = err.Error()
logx.WithContext(ctx).WithFields(logFields).Error(err.Error())
}
}
// 记录业务日志
func (l *StructuredLogger) LogInfo(ctx context.Context, message string, fields LogFields) {
logFields := l.buildBaseFields(ctx, fields)
logx.WithContext(ctx).WithFields(logFields).Info(message)
}
// 记录警告日志
func (l *StructuredLogger) LogWarn(ctx context.Context, message string, fields LogFields) {
logFields := l.buildBaseFields(ctx, fields)
logx.WithContext(ctx).WithFields(logFields).Slow(message)
}
// 构建基础日志字段
func (l *StructuredLogger) buildBaseFields(ctx context.Context, fields LogFields) logx.LogField {
baseFields := logx.LogField{
"service": l.service,
"method": l.method,
}
// 添加链路追踪信息
if traceId := trace.TraceIDFromContext(ctx); traceId != "" {
baseFields["trace_id"] = traceId
}
if spanId := trace.SpanIDFromContext(ctx); spanId != "" {
baseFields["span_id"] = spanId
}
// 合并自定义字段
for k, v := range fields {
baseFields[k] = v
}
return baseFields
}
```
### 3.2 日志中间件
```go
// shared/middleware/log_middleware.go
package middleware
import (
"context"
"net/http"
"time"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan/shared/logger"
)
// HTTP日志中间件
func LogMiddleware(serviceName string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
logger := logger.NewStructuredLogger(serviceName, r.URL.Path)
// 记录请求开始
logger.LogInfo(r.Context(), "request_start", logger.LogFields{
"method": r.Method,
"path": r.URL.Path,
"query": r.URL.RawQuery,
"user_agent": r.UserAgent(),
"remote_ip": httpx.GetRemoteAddr(r),
})
// 创建响应包装器
wrapper := &responseWrapper{
ResponseWriter: w,
statusCode: http.StatusOK,
}
// 执行请求
next.ServeHTTP(wrapper, r)
// 记录请求结束
duration := time.Since(start)
fields := logger.LogFields{
"status_code": wrapper.statusCode,
"duration_ms": duration.Milliseconds(),
}
if wrapper.statusCode >= 400 {
logger.LogWarn(r.Context(), "request_error", fields)
} else {
logger.LogInfo(r.Context(), "request_success", fields)
}
})
}
}
```
## 4. 使用示例
### 4.1 在 Handler 中使用
```go
// client/internal/handler/product/getproductlisthandler.go
func (h *GetProductListHandler) GetProductList(w http.ResponseWriter, r *http.Request) {
// 创建错误构造器
errBuilder := errcode.NewErrorBuilder("client-api", "GetProductList")
logger := logger.NewStructuredLogger("client-api", "GetProductList")
var req types.GetProductListReq
// 参数校验
if err := validator.ValidateAndParse(r, &req); err != nil {
appErr := errBuilder.ValidationError("PARAM_INVALID", "参数校验失败", err)
logger.LogError(r.Context(), appErr, logger.LogFields{
"request": req,
})
response.ErrorResponse(w, appErr)
return
}
// 调用Logic层
resp, err := h.logic.GetProductList(r.Context(), &req)
if err != nil {
logger.LogError(r.Context(), err, logger.LogFields{
"request": req,
})
response.ErrorResponse(w, err)
return
}
// 记录成功日志
logger.LogInfo(r.Context(), "get_product_list_success", logger.LogFields{
"request": req,
"result_count": len(resp.List),
})
response.SuccessResponse(w, resp)
}
```
### 4.2 在 RPC Logic 中使用
```go
// domains/product/rpc/internal/logic/getproductlistlogic.go
func (l *GetProductListLogic) GetProductList(ctx context.Context, req *product.GetProductListReq) (*product.GetProductListResp, error) {
errBuilder := errcode.NewErrorBuilder("product-rpc", "GetProductList")
logger := logger.NewStructuredLogger("product-rpc", "GetProductList")
// 业务校验
validator := validator.NewProductValidator(ctx, l.svcCtx)
if err := validator.ValidateGetProductListRequest(req); err != nil {
appErr := errBuilder.BusinessError("VALIDATION_FAILED", err.Error())
logger.LogError(ctx, appErr, logger.LogFields{
"request": req,
})
return nil, appErr
}
// 查询数据库
products, err := l.svcCtx.ProductModel.FindList(ctx, req)
if err != nil {
appErr := errBuilder.SystemError("DB_QUERY_FAILED", "查询产品列表失败", err)
logger.LogError(ctx, appErr, logger.LogFields{
"request": req,
"db_error": err.Error(),
})
return nil, appErr
}
logger.LogInfo(ctx, "get_product_list_success", logger.LogFields{
"request": req,
"result_count": len(products),
})
return &product.GetProductListResp{
List: products,
Total: int64(len(products)),
}, nil
}
```
## 5. 监控和告警
### 5.1 错误监控配置
```go
// shared/monitor/error_monitor.go
package monitor
import (
"context"
"github.com/zeromicro/go-zero/core/metric"
"tianyuan/shared/errcode"
)
var (
// 错误计数器
ErrorCounter = metric.NewCounterVec(&metric.CounterVecOpts{
Namespace: "tianyuan",
Subsystem: "error",
Name: "total",
Help: "Total number of errors",
Labels: []string{"service", "type", "level", "code"},
})
// 错误率直方图
ErrorRateHistogram = metric.NewHistogramVec(&metric.HistogramVecOpts{
Namespace: "tianyuan",
Subsystem: "error",
Name: "rate",
Help: "Error rate histogram",
Labels: []string{"service", "method"},
})
)
// 记录错误指标
func RecordError(appErr *errcode.AppError) {
ErrorCounter.Inc(
appErr.Service,
string(appErr.Type),
appErr.Level.String(),
appErr.Code,
)
}
```
### 5.2 告警规则
```yaml
# prometheus告警规则
groups:
- name: tianyuan-errors
rules:
# 错误率告警
- alert: HighErrorRate
expr: rate(tianyuan_error_total[5m]) > 0.1
for: 2m
labels:
severity: warning
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value }} for service {{ $labels.service }}"
# 致命错误告警
- alert: FatalError
expr: increase(tianyuan_error_total{level="FATAL"}[1m]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "Fatal error detected"
description: "Fatal error in service {{ $labels.service }}: {{ $labels.code }}"
```
## 6. 最佳实践总结
1. **错误分级原则**
- DEBUG/INFO开发调试信息
- WARN需要关注但不影响业务
- ERROR业务错误需要处理
- FATAL/PANIC系统级错误需要立即处理
2. **链路追踪要点**
- 每个请求都有唯一的 TraceId
- 跨服务调用保持链路连续性
- 关键操作添加自定义 Span
3. **日志记录规范**
- 结构化日志,便于查询分析
- 包含链路追踪信息
- 敏感信息脱敏处理
4. **监控告警策略**
- 错误率监控
- 关键错误实时告警
- 链路追踪性能监控

544
validator封装设计.md Normal file
View File

@@ -0,0 +1,544 @@
# Validator 封装设计
## 1. 整体架构设计
```
shared/
├── validator/ # 格式校验器封装
│ ├── validator.go # 校验器初始化和接口定义
│ ├── format_validator.go # 格式校验实现
│ ├── custom_rules.go # 自定义校验规则
│ ├── messages.go # 错误消息配置
│ └── middleware.go # 校验中间件
├── errcode/
│ ├── validator_errors.go # 校验相关错误码
└── response/
└── validator_response.go # 校验错误响应格式
domains/
└── product/rpc/internal/logic/
└── validator/ # 业务校验器封装
├── base.go # 业务校验器基类
├── product_validator.go
└── category_validator.go
```
## 2. 格式校验器封装shared/validator
### 2.1 校验器接口定义
```go
// shared/validator/validator.go
package validator
import (
"reflect"
"github.com/go-playground/validator/v10"
"tianyuan/shared/errcode"
)
// 校验器接口
type IValidator interface {
Validate(data interface{}) error
ValidateStruct(data interface{}) error
AddCustomRule(tag string, fn validator.Func) error
}
// 全局校验器实例
var GlobalValidator IValidator
// 初始化校验器
func Init() error {
v := &FormatValidator{
validator: validator.New(),
}
// 注册自定义规则
if err := v.registerCustomRules(); err != nil {
return err
}
// 注册中文错误消息
if err := v.registerMessages(); err != nil {
return err
}
GlobalValidator = v
return nil
}
// 便捷函数
func Validate(data interface{}) error {
return GlobalValidator.Validate(data)
}
func ValidateStruct(data interface{}) error {
return GlobalValidator.ValidateStruct(data)
}
```
### 2.2 格式校验器实现
```go
// shared/validator/format_validator.go
package validator
import (
"fmt"
"reflect"
"strings"
"github.com/go-playground/validator/v10"
"tianyuan/shared/errcode"
)
type FormatValidator struct {
validator *validator.Validate
}
// 校验接口实现
func (v *FormatValidator) Validate(data interface{}) error {
if err := v.validator.Struct(data); err != nil {
return v.formatError(err)
}
return nil
}
func (v *FormatValidator) ValidateStruct(data interface{}) error {
return v.Validate(data)
}
func (v *FormatValidator) AddCustomRule(tag string, fn validator.Func) error {
return v.validator.RegisterValidation(tag, fn)
}
// 错误格式化
func (v *FormatValidator) formatError(err error) error {
if validationErrors, ok := err.(validator.ValidationErrors); ok {
var errMsgs []string
for _, fieldError := range validationErrors {
errMsg := v.getErrorMessage(fieldError)
errMsgs = append(errMsgs, errMsg)
}
return errcode.NewValidationError(strings.Join(errMsgs, "; "))
}
return err
}
// 获取错误消息
func (v *FormatValidator) getErrorMessage(fieldError validator.FieldError) string {
// 获取字段的中文名称
fieldName := v.getFieldName(fieldError)
// 根据校验标签获取错误消息
switch fieldError.Tag() {
case "required":
return fmt.Sprintf("%s不能为空", fieldName)
case "min":
return fmt.Sprintf("%s最小值为%s", fieldName, fieldError.Param())
case "max":
return fmt.Sprintf("%s最大值为%s", fieldName, fieldError.Param())
case "email":
return fmt.Sprintf("%s格式不正确", fieldName)
case "mobile":
return fmt.Sprintf("%s格式不正确", fieldName)
default:
return fmt.Sprintf("%s校验失败", fieldName)
}
}
// 获取字段中文名称
func (v *FormatValidator) getFieldName(fieldError validator.FieldError) string {
// 可以通过反射获取struct tag中的中文名称
// 或者维护一个字段名映射表
fieldName := fieldError.Field()
// 简单示例,实际可以更复杂
nameMap := map[string]string{
"CategoryId": "分类ID",
"PageNum": "页码",
"PageSize": "每页数量",
"Keyword": "关键词",
"Mobile": "手机号",
"Email": "邮箱",
}
if chineseName, exists := nameMap[fieldName]; exists {
return chineseName
}
return fieldName
}
```
### 2.3 自定义校验规则
```go
// shared/validator/custom_rules.go
package validator
import (
"regexp"
"github.com/go-playground/validator/v10"
)
// 注册自定义校验规则
func (v *FormatValidator) registerCustomRules() error {
// 手机号校验
if err := v.validator.RegisterValidation("mobile", validateMobile); err != nil {
return err
}
// 身份证号校验
if err := v.validator.RegisterValidation("idcard", validateIDCard); err != nil {
return err
}
// 企业统一社会信用代码校验
if err := v.validator.RegisterValidation("creditcode", validateCreditCode); err != nil {
return err
}
return nil
}
// 手机号校验函数
func validateMobile(fl validator.FieldLevel) bool {
mobile := fl.Field().String()
pattern := `^1[3-9]\d{9}$`
matched, _ := regexp.MatchString(pattern, mobile)
return matched
}
// 身份证号校验函数
func validateIDCard(fl validator.FieldLevel) bool {
idcard := fl.Field().String()
pattern := `^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$`
matched, _ := regexp.MatchString(pattern, idcard)
return matched
}
// 企业统一社会信用代码校验函数
func validateCreditCode(fl validator.FieldLevel) bool {
code := fl.Field().String()
pattern := `^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$`
matched, _ := regexp.MatchString(pattern, code)
return matched
}
```
### 2.4 校验中间件
```go
// shared/validator/middleware.go
package validator
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"tianyuan/shared/response"
)
// 校验中间件
func ValidationMiddleware() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 这里可以添加全局校验逻辑
// 比如请求头校验、通用参数校验等
next.ServeHTTP(w, r)
})
}
}
// Handler校验辅助函数
func ValidateAndParse(r *http.Request, req interface{}) error {
// 1. 参数绑定
if err := httpx.Parse(r, req); err != nil {
return err
}
// 2. 格式校验
if err := Validate(req); err != nil {
return err
}
return nil
}
```
## 3. 业务校验器封装(各域内部)
### 3.1 业务校验器基类
```go
// domains/product/rpc/internal/logic/validator/base.go
package validator
import (
"context"
"tianyuan/domains/product/rpc/internal/svc"
"tianyuan/shared/errcode"
)
// 业务校验器基类
type BaseValidator struct {
svcCtx *svc.ServiceContext
ctx context.Context
}
func NewBaseValidator(ctx context.Context, svcCtx *svc.ServiceContext) *BaseValidator {
return &BaseValidator{
svcCtx: svcCtx,
ctx: ctx,
}
}
// 业务校验接口
type IBusinessValidator interface {
ValidatePermission(userId int64, action string) error
ValidateResourceExists(resourceType string, resourceId int64) error
ValidateBusinessRules(data interface{}) error
}
// 通用业务校验方法
func (v *BaseValidator) ValidatePermission(userId int64, action string) error {
// 调用用户域RPC检查权限
resp, err := v.svcCtx.UserRpc.CheckPermission(v.ctx, &user.CheckPermissionReq{
UserId: userId,
Permission: action,
})
if err != nil {
return err
}
if !resp.HasPermission {
return errcode.NewBusinessError("用户无权限执行此操作")
}
return nil
}
func (v *BaseValidator) ValidateResourceExists(resourceType string, resourceId int64) error {
// 根据资源类型检查资源是否存在
switch resourceType {
case "category":
return v.validateCategoryExists(resourceId)
case "product":
return v.validateProductExists(resourceId)
default:
return errcode.NewBusinessError("未知的资源类型")
}
}
func (v *BaseValidator) validateCategoryExists(categoryId int64) error {
category, err := v.svcCtx.CategoryModel.FindOne(v.ctx, categoryId)
if err != nil {
return errcode.NewBusinessError("分类不存在")
}
if category.Status != 1 {
return errcode.NewBusinessError("分类已禁用")
}
return nil
}
func (v *BaseValidator) validateProductExists(productId int64) error {
product, err := v.svcCtx.ProductModel.FindOne(v.ctx, productId)
if err != nil {
return errcode.NewBusinessError("产品不存在")
}
if product.Status != 1 {
return errcode.NewBusinessError("产品已下架")
}
return nil
}
```
### 3.2 具体业务校验器
```go
// domains/product/rpc/internal/logic/validator/product_validator.go
package validator
import (
"context"
"tianyuan/domains/product/rpc/internal/svc"
"tianyuan/domains/product/rpc/product"
"tianyuan/shared/errcode"
)
type ProductValidator struct {
*BaseValidator
}
func NewProductValidator(ctx context.Context, svcCtx *svc.ServiceContext) *ProductValidator {
return &ProductValidator{
BaseValidator: NewBaseValidator(ctx, svcCtx),
}
}
// 校验获取产品列表请求
func (v *ProductValidator) ValidateGetProductListRequest(req *product.GetProductListReq) error {
// 1. 校验用户权限
if err := v.ValidatePermission(req.UserId, "product:list"); err != nil {
return err
}
// 2. 校验分类存在性
if req.CategoryId > 0 {
if err := v.ValidateResourceExists("category", req.CategoryId); err != nil {
return err
}
}
// 3. 校验用户是否有查看该分类的权限
if req.CategoryId > 0 {
if err := v.validateCategoryAccess(req.UserId, req.CategoryId); err != nil {
return err
}
}
return nil
}
// 校验创建产品请求
func (v *ProductValidator) ValidateCreateProductRequest(req *product.CreateProductReq) error {
// 1. 校验用户权限
if err := v.ValidatePermission(req.UserId, "product:create"); err != nil {
return err
}
// 2. 校验分类存在性
if err := v.ValidateResourceExists("category", req.CategoryId); err != nil {
return err
}
// 3. 校验产品名称是否重复
if err := v.validateProductNameUnique(req.Name, req.CategoryId); err != nil {
return err
}
return nil
}
// 校验分类访问权限
func (v *ProductValidator) validateCategoryAccess(userId, categoryId int64) error {
// 检查用户是否有访问该分类的权限
// 这里可能涉及到用户等级、VIP权限等业务逻辑
userInfo, err := v.svcCtx.UserRpc.GetUserInfo(v.ctx, &user.GetUserInfoReq{
UserId: userId,
})
if err != nil {
return err
}
// 示例VIP用户可以访问所有分类普通用户只能访问基础分类
category, err := v.svcCtx.CategoryModel.FindOne(v.ctx, categoryId)
if err != nil {
return err
}
if category.RequireVip && userInfo.UserType != "vip" {
return errcode.NewBusinessError("该分类需要VIP权限")
}
return nil
}
// 校验产品名称唯一性
func (v *ProductValidator) validateProductNameUnique(name string, categoryId int64) error {
exists, err := v.svcCtx.ProductModel.CheckNameExists(v.ctx, name, categoryId)
if err != nil {
return err
}
if exists {
return errcode.NewBusinessError("同分类下产品名称已存在")
}
return nil
}
```
## 4. 使用示例
### 4.1 在 Handler 中使用格式校验
```go
// client/internal/handler/product/getproductlisthandler.go
func (h *GetProductListHandler) GetProductList(w http.ResponseWriter, r *http.Request) {
var req types.GetProductListReq
// 使用封装的校验函数
if err := validator.ValidateAndParse(r, &req); err != nil {
response.ParamErrorResponse(w, err)
return
}
// 调用Logic层
resp, err := h.logic.GetProductList(&req)
if err != nil {
response.ErrorResponse(w, err)
return
}
response.SuccessResponse(w, resp)
}
```
### 4.2 在 RPC Logic 中使用业务校验
```go
// domains/product/rpc/internal/logic/getproductlistlogic.go
func (l *GetProductListLogic) GetProductList(req *product.GetProductListReq) (*product.GetProductListResp, error) {
// 创建业务校验器
validator := validator.NewProductValidator(l.ctx, l.svcCtx)
// 执行业务校验
if err := validator.ValidateGetProductListRequest(req); err != nil {
return nil, err
}
// 执行业务逻辑
products, total, err := l.svcCtx.ProductModel.FindList(l.ctx, req)
if err != nil {
return nil, err
}
return &product.GetProductListResp{
List: products,
Total: total,
}, nil
}
```
### 4.3 在 main.go 中初始化
```go
// client/client.go
func main() {
// 初始化格式校验器
if err := validator.Init(); err != nil {
log.Fatalf("初始化校验器失败: %v", err)
}
// 其他初始化代码...
}
```
## 5. 封装的优势
1. **统一的接口**:所有校验都通过统一的接口调用
2. **错误处理标准化**:统一的错误格式和消息
3. **可扩展性**:容易添加新的校验规则
4. **代码复用**:通用校验逻辑可以复用
5. **测试友好**:校验器可以独立测试
6. **配置化**:错误消息和校验规则可以配置化管理
这样的封装既保持了格式校验和业务校验的清晰边界,又提供了便捷的使用接口。

View File

@@ -0,0 +1,405 @@
# 域与服务的关系及目录架构设计
## 🤔 域 vs 服务的关系
### 核心概念
- **域 (Domain)** = **业务边界**,是逻辑概念
- **服务 (Service)** = **技术实现**,是物理部署单元
### 关系图解
```
Domain (域)
├── Service A (微服务A)
├── Service B (微服务B)
└── Service C (微服务C)
```
**一个域可以包含多个微服务,也可以是一个微服务。**
## 🏗️ 具体的域-服务映射
### 方案一:一域一服务 (推荐给中小型项目)
```
├── user-domain/ # 用户域
│ └── user-service/ # 用户服务 (包含用户+企业+认证)
├── security-domain/ # 安全域
│ └── security-service/ # 安全服务 (加密+白名单+密钥)
├── product-domain/ # 产品域
│ └── product-service/ # 产品服务 (产品管理+权限控制)
├── data-domain/ # 数据服务域
│ └── data-service/ # 数据服务 (统一数据网关)
├── billing-domain/ # 计费域
│ └── billing-service/ # 计费服务 (钱包+扣费+账单)
├── audit-domain/ # 审计域
│ └── audit-service/ # 审计服务 (日志+记录)
└── gateway-domain/ # 网关域
└── gateway-service/ # 网关服务
```
### 方案二:一域多服务 (适合大型项目)
```
├── user-domain/ # 用户域
│ ├── user-service/ # 用户管理服务
│ ├── enterprise-service/ # 企业认证服务
│ └── auth-service/ # 认证授权服务
├── security-domain/ # 安全域
│ ├── encryption-service/ # 加密解密服务
│ ├── whitelist-service/ # 白名单服务
│ └── key-management-service/ # 密钥管理服务
├── data-domain/ # 数据服务域
│ ├── data-gateway-service/ # 数据网关服务
│ ├── risk-service/ # 风险评估服务 (原FLXG)
│ ├── credit-service/ # 征信服务 (原JRZQ)
│ ├── company-service/ # 企业信息服务 (原QYGL)
│ └── query-service/ # 数据查询服务 (原IVYZ)
└── ...
```
## 📁 推荐的目录架构设计
### 整体项目结构 (基于你的项目)
```
tianyuan-api-platform/
├── domains/ # 业务域目录
│ ├── user-domain/ # 用户域
│ │ └── user-service/ # 用户服务
│ │ ├── cmd/ # 应用入口
│ │ │ ├── server/main.go # 服务器入口
│ │ │ ├── cli/main.go # CLI工具
│ │ │ └── migrator/main.go # 数据库迁移
│ │ ├── api/ # API定义
│ │ │ ├── grpc/ # gRPC定义
│ │ │ │ └── user.proto
│ │ │ └── http/ # HTTP API定义
│ │ │ └── user.api
│ │ ├── internal/ # 内部实现
│ │ │ ├── domain/ # 领域层
│ │ │ │ ├── entity/ # 实体
│ │ │ │ │ ├── user.go
│ │ │ │ │ └── enterprise.go
│ │ │ │ ├── valueobject/ # 值对象
│ │ │ │ ├── repository/ # 仓储接口
│ │ │ │ │ └── user_repository.go
│ │ │ │ └── service/ # 领域服务
│ │ │ │ └── user_domain_service.go
│ │ │ ├── application/ # 应用层
│ │ │ │ ├── service/ # 应用服务
│ │ │ │ │ └── user_app_service.go
│ │ │ │ ├── dto/ # 数据传输对象
│ │ │ │ └── command/ # 命令处理
│ │ │ ├── infrastructure/ # 基础设施层
│ │ │ │ ├── persistence/ # 持久化
│ │ │ │ │ ├── mysql/
│ │ │ │ │ │ └── user_repository_impl.go
│ │ │ │ │ └── redis/
│ │ │ │ ├── messaging/ # 消息队列
│ │ │ │ └── external/ # 外部服务
│ │ │ └── interfaces/ # 接口适配器
│ │ │ ├── grpc/ # gRPC适配器
│ │ │ │ └── user_grpc_handler.go
│ │ │ ├── http/ # HTTP适配器
│ │ │ │ └── user_http_handler.go
│ │ │ └── event/ # 事件处理
│ │ ├── configs/ # 配置文件
│ │ │ ├── config.yaml
│ │ │ └── config.dev.yaml
│ │ ├── deployments/ # 部署配置
│ │ │ ├── Dockerfile
│ │ │ └── k8s/
│ │ ├── docs/ # 文档
│ │ ├── test/ # 测试
│ │ └── go.mod
│ │
│ ├── data-domain/ # 数据服务域
│ │ └── data-service/ # 数据服务 (主要协调者)
│ │ ├── cmd/
│ │ │ └── server/main.go
│ │ ├── api/
│ │ │ └── http/
│ │ │ └── data.api
│ │ ├── internal/
│ │ │ ├── domain/
│ │ │ │ └── service/
│ │ │ │ ├── risk_assessment_service.go # 原FLXG逻辑
│ │ │ │ ├── credit_check_service.go # 原JRZQ逻辑
│ │ │ │ ├── company_info_service.go # 原QYGL逻辑
│ │ │ │ ├── data_query_service.go # 原IVYZ逻辑
│ │ │ │ └── app_system_service.go # 原YYSY逻辑
│ │ │ ├── application/
│ │ │ │ └── service/
│ │ │ │ └── data_orchestrator_service.go # 主协调器
│ │ │ ├── infrastructure/
│ │ │ │ └── external/
│ │ │ │ ├── westdex_client.go # 西部数据源客户端
│ │ │ │ ├── baidu_client.go # 百度API客户端
│ │ │ │ └── aliyun_client.go # 阿里云API客户端
│ │ │ └── interfaces/
│ │ │ └── http/
│ │ │ ├── risk_handler.go # /api/v1/data/risk-assessment
│ │ │ ├── credit_handler.go # /api/v1/data/credit-check
│ │ │ ├── company_handler.go # /api/v1/data/company-info
│ │ │ └── query_handler.go # /api/v1/data/query
│ │ └── ...
│ │
│ ├── security-domain/ # 安全域
│ │ └── security-service/
│ │ ├── cmd/
│ │ ├── api/grpc/
│ │ │ └── security.proto
│ │ ├── internal/
│ │ │ ├── domain/
│ │ │ │ └── service/
│ │ │ │ ├── encryption_service.go # 加密解密
│ │ │ │ ├── whitelist_service.go # 白名单管理
│ │ │ │ └── key_management_service.go # 密钥管理
│ │ │ └── ...
│ │ └── ...
│ │
│ ├── billing-domain/ # 计费域
│ │ └── billing-service/
│ │ ├── cmd/
│ │ ├── api/grpc/
│ │ │ └── billing.proto
│ │ ├── internal/
│ │ │ ├── domain/
│ │ │ │ ├── entity/
│ │ │ │ │ ├── wallet.go
│ │ │ │ │ ├── transaction.go
│ │ │ │ │ └── billing_record.go
│ │ │ │ └── service/
│ │ │ │ ├── wallet_service.go
│ │ │ │ ├── charging_service.go
│ │ │ │ └── billing_service.go
│ │ │ └── ...
│ │ └── ...
│ │
│ ├── product-domain/ # 产品域
│ │ └── product-service/
│ │ └── ... (类似结构)
│ │
│ ├── audit-domain/ # 审计域
│ │ └── audit-service/
│ │ └── ... (类似结构)
│ │
│ └── gateway-domain/ # 网关域
│ └── gateway-service/
│ ├── cmd/
│ │ └── server/main.go
│ ├── internal/
│ │ ├── middleware/ # 中间件
│ │ │ ├── auth.go
│ │ │ ├── rate_limit.go
│ │ │ └── audit.go
│ │ ├── router/ # 路由器
│ │ │ └── gateway_router.go
│ │ └── proxy/ # 代理
│ │ └── service_proxy.go
│ └── configs/
│ └── gateway.yaml
├── shared/ # 共享库
│ ├── pkg/ # 通用包
│ │ ├── crypto/ # 加密工具
│ │ ├── jwt/ # JWT工具
│ │ ├── response/ # 响应格式
│ │ ├── errors/ # 错误定义
│ │ ├── logger/ # 日志工具
│ │ └── utils/ # 工具函数
│ ├── events/ # 领域事件定义
│ │ ├── user_events.go
│ │ ├── billing_events.go
│ │ └── audit_events.go
│ └── proto/ # 公共protobuf定义
│ └── common.proto
├── infrastructure/ # 基础设施
│ ├── docker-compose.yaml # 本地开发环境
│ ├── kubernetes/ # K8s部署文件
│ │ ├── user-service.yaml
│ │ ├── data-service.yaml
│ │ └── gateway-service.yaml
│ ├── terraform/ # 基础设施即代码
│ └── monitoring/ # 监控配置
│ ├── prometheus/
│ └── grafana/
├── tools/ # 工具和脚本
│ ├── generate/ # 代码生成器
│ ├── migration/ # 数据迁移脚本
│ └── deploy/ # 部署脚本
├── docs/ # 项目文档
│ ├── architecture/ # 架构文档
│ ├── api/ # API文档
│ └── deployment/ # 部署文档
├── scripts/ # 构建和部署脚本
│ ├── build.sh
│ ├── deploy.sh
│ └── test.sh
├── .github/ # GitHub Actions
│ └── workflows/
│ ├── user-service.yml
│ ├── data-service.yml
│ └── gateway-service.yml
├── Makefile # 统一构建脚本
├── docker-compose.yml # 整体服务编排
└── README.md # 项目说明
```
## 🔄 从当前项目的迁移策略
### 当前项目映射关系
```
当前结构 → 新结构
apps/api/ → domains/data-domain/data-service/
├── FLXG/ → internal/domain/service/risk_assessment_service.go
├── JRZQ/ → internal/domain/service/credit_check_service.go
├── QYGL/ → internal/domain/service/company_info_service.go
├── IVYZ/ → internal/domain/service/data_query_service.go
└── YYSY/ → internal/domain/service/app_system_service.go
apps/user/ → domains/user-domain/user-service/
apps/sentinel/ → domains/product-domain/product-service/
apps/gateway/ → domains/gateway-domain/gateway-service/
apps/admin/ → domains/admin-domain/admin-service/
apps/mqs/ → domains/audit-domain/audit-service/
pkg/ → shared/pkg/
westDex/ → domains/data-domain/data-service/internal/infrastructure/external/
```
### 逐步迁移计划
#### 阶段 1: 创建新的目录结构 (1 周)
```bash
# 创建新的目录结构
mkdir -p domains/{user,data,security,billing,product,audit,gateway}-domain
mkdir -p shared/{pkg,events,proto}
mkdir -p infrastructure/{kubernetes,terraform,monitoring}
```
#### 阶段 2: 迁移共享包 (1 周)
```bash
# 迁移共享代码
mv pkg/ shared/pkg/
mv westDex/ domains/data-domain/data-service/internal/infrastructure/external/westdex/
```
#### 阶段 3: 重构数据服务域 (2-3 周)
```go
// 创建数据服务协调器
// domains/data-domain/data-service/internal/application/service/data_orchestrator.go
type DataOrchestrator struct {
riskService *RiskAssessmentService // 原FLXG
creditService *CreditCheckService // 原JRZQ
companyService *CompanyInfoService // 原QYGL
queryService *DataQueryService // 原IVYZ
appService *AppSystemService // 原YYSY
// 依赖的其他域服务
securityService SecurityServiceClient // gRPC客户端
userService UserServiceClient // gRPC客户端
productService ProductServiceClient // gRPC客户端
billingService BillingServiceClient // gRPC客户端
auditService AuditServiceClient // gRPC客户端
}
func (d *DataOrchestrator) ProcessAPIRequest(ctx context.Context, req *APIRequest) (*APIResponse, error) {
// 之前分析的完整流程代码
}
```
#### 阶段 4: 拆分其他域服务 (3-4 周)
逐个创建和迁移其他域服务
#### 阶段 5: 完善基础设施 (2 周)
添加监控、CI/CD、部署等
## 🚀 实际开发工作流
### 1. 单个服务开发
```bash
cd domains/user-domain/user-service
# 开发
go run cmd/server/main.go
# 测试
go test ./...
# 构建
go build -o bin/user-service cmd/server/main.go
# 部署
docker build -t user-service .
```
### 2. 跨域开发
```bash
# 启动依赖服务
docker-compose up mysql redis kafka
# 启动相关服务
make start-user-service
make start-security-service
make start-data-service
# 集成测试
make test-integration
```
### 3. 统一管理
```makefile
# Makefile
.PHONY: build-all start-all test-all
build-all:
cd domains/user-domain/user-service && go build -o ../../../bin/user-service cmd/server/main.go
cd domains/data-domain/data-service && go build -o ../../../bin/data-service cmd/server/main.go
cd domains/gateway-domain/gateway-service && go build -o ../../../bin/gateway-service cmd/server/main.go
start-all:
docker-compose up -d
test-all:
cd domains/user-domain/user-service && go test ./...
cd domains/data-domain/data-service && go test ./...
cd domains/gateway-domain/gateway-service && go test ./...
```
## 🎯 总结
### 域与服务的关系:
- **域 = 业务边界** (逻辑概念)
- **服务 = 部署单元** (物理实现)
- **推荐:一域一服务** (对于你的项目规模)
### 目录架构核心原则:
1. **按域组织** - 顶层按业务域划分
2. **标准结构** - 每个服务内部结构一致
3. **清晰分层** - 领域层、应用层、基础设施层、接口层
4. **共享复用** - 公共代码放在 shared 目录
5. **独立部署** - 每个服务都可以独立构建部署
这种架构既保持了逻辑上的清晰性,又确保了技术实现的可操作性!

View File

@@ -0,0 +1,418 @@
# 基于 go-zero 的完整微服务架构设计
## 🏗️ 整体架构图
```
┌─────────────────────────────────────────────────────────┐
│ External Access │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Web App │ │Mobile │ │3rd API │ │Admin │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────│────────────│─────────────│─────────────│───────┘
│ │ │ │
└────────────┼─────────────┼─────────────┘
│ │
┌──────▼─────────────▼──────┐
│ Gateway API (HTTP) │ 🌐 HTTP入口
└──────┬────────────────────┘
┌───────────┼───────────┐
│ │ │
┌────▼────┐ ┌───▼───┐ ┌─────▼─────┐
│User RPC │ │Data │ │Admin HTTP │ 🔥 核心服务
│ │ │RPC │ │ │
└────┬────┘ └───┬───┘ └─────┬─────┘
│ │ │
┌────▼────┐ ┌───▼───┐ ┌─────▼─────┐
│Security │ │Billing│ │Audit MQS │ 🛡️ 支撑服务
│RPC │ │RPC │ │ │
└─────────┘ └───────┘ └───────────┘
```
## 📁 完整目录结构
```
tianyuan-microservice/
├── api/ # HTTP API服务
│ ├── gateway/ # 🌐 API网关 (唯一HTTP入口)
│ │ ├── gateway.api # API定义
│ │ ├── gateway.go # 启动文件
│ │ ├── etc/
│ │ │ ├── gateway.yaml # 生产配置
│ │ │ └── gateway-dev.yaml # 开发配置
│ │ ├── internal/
│ │ │ ├── config/config.go # 配置结构
│ │ │ ├── handler/ # HTTP处理器
│ │ │ │ ├── auth/
│ │ │ │ │ ├── loginhandler.go
│ │ │ │ │ └── logouthandler.go
│ │ │ │ ├── data/
│ │ │ │ │ ├── riskhandler.go # /api/v1/data/risk
│ │ │ │ │ ├── credithandler.go # /api/v1/data/credit
│ │ │ │ │ └── companyhandler.go # /api/v1/data/company
│ │ │ │ ├── user/
│ │ │ │ │ └── userinfohandler.go
│ │ │ │ └── routes.go # 路由配置
│ │ │ ├── logic/ # 业务逻辑 (协调RPC调用)
│ │ │ │ ├── auth/
│ │ │ │ │ └── loginlogic.go
│ │ │ │ ├── data/
│ │ │ │ │ ├── risklogic.go # 协调data-rpc
│ │ │ │ │ ├── creditlogic.go
│ │ │ │ │ └── companylogic.go
│ │ │ │ └── user/
│ │ │ │ └── userinfologic.go
│ │ │ ├── middleware/ # 中间件
│ │ │ │ ├── authinterceptor.go # JWT认证
│ │ │ │ ├── ratelimit.go # 限流
│ │ │ │ └── audit.go # 审计
│ │ │ ├── svc/servicecontext.go # 服务上下文 (包含所有RPC客户端)
│ │ │ └── types/types.go # 请求响应类型
│ │ ├── Dockerfile
│ │ └── k8s/
│ │ └── deployment.yaml
│ │
│ └── admin/ # 🏢 管理后台 (独立HTTP服务)
│ ├── admin.api
│ ├── admin.go
│ ├── etc/admin.yaml
│ ├── internal/
│ │ ├── handler/
│ │ │ ├── product/
│ │ │ │ ├── createproducthandler.go
│ │ │ │ └── listproducthandler.go
│ │ │ ├── review/
│ │ │ │ └── reviewenterprisehandler.go
│ │ │ └── routes.go
│ │ ├── logic/
│ │ ├── svc/servicecontext.go # 包含RPC客户端
│ │ └── types/types.go
│ ├── Dockerfile
│ └── k8s/deployment.yaml
├── rpc/ # RPC微服务集群
│ ├── user/ # 👥 用户域RPC服务
│ │ ├── user.proto # gRPC定义
│ │ ├── user.go # 启动文件
│ │ ├── etc/user.yaml # 配置
│ │ ├── internal/
│ │ │ ├── config/config.go
│ │ │ ├── logic/ # 业务逻辑
│ │ │ │ ├── auth/
│ │ │ │ │ ├── loginlogic.go
│ │ │ │ │ └── validatetokenlogic.go
│ │ │ │ ├── user/
│ │ │ │ │ ├── getuserinfologic.go
│ │ │ │ │ └── updateuserlogic.go
│ │ │ │ └── enterprise/
│ │ │ │ ├── validateenterpriselogic.go
│ │ │ │ └── getenterpriselogic.go
│ │ │ ├── server/userserver.go # gRPC服务实现
│ │ │ ├── svc/servicecontext.go # 服务上下文
│ │ │ └── model/ # 数据模型
│ │ │ ├── usermodel.go # 用户表模型
│ │ │ ├── enterprisemodel.go # 企业表模型
│ │ │ └── vars.go # 常量定义
│ │ ├── pb/ # 生成的protobuf文件
│ │ │ ├── user.pb.go
│ │ │ └── user_grpc.pb.go
│ │ ├── Dockerfile
│ │ └── k8s/deployment.yaml
│ │
│ ├── data/ # 📊 数据服务域RPC服务 (核心协调者)
│ │ ├── data.proto
│ │ ├── data.go
│ │ ├── etc/data.yaml
│ │ ├── internal/
│ │ │ ├── logic/
│ │ │ │ ├── orchestrator/
│ │ │ │ │ └── dataorchestratorlogic.go # 🔥 核心协调逻辑
│ │ │ │ ├── risk/ # 原FLXG业务
│ │ │ │ │ ├── riskassessmentlogic.go
│ │ │ │ │ └── riskreportlogic.go
│ │ │ │ ├── credit/ # 原JRZQ业务
│ │ │ │ │ ├── creditchecklogic.go
│ │ │ │ │ └── credithistorylogic.go
│ │ │ │ ├── company/ # 原QYGL业务
│ │ │ │ │ ├── companyinfologic.go
│ │ │ │ │ └── companysearchlogic.go
│ │ │ │ └── query/ # 原IVYZ业务
│ │ │ │ ├── dataquerylogic.go
│ │ │ │ └── batchquerylogic.go
│ │ │ ├── server/dataserver.go
│ │ │ ├── svc/servicecontext.go # 包含其他RPC客户端
│ │ │ └── external/ # 外部API客户端
│ │ │ ├── westdex/westdexclient.go # 西部数据源
│ │ │ ├── baidu/baiduclient.go # 百度API
│ │ │ └── aliyun/aliyunclient.go # 阿里云API
│ │ ├── pb/
│ │ ├── Dockerfile
│ │ └── k8s/deployment.yaml
│ │
│ ├── security/ # 🔐 安全域RPC服务
│ │ ├── security.proto
│ │ ├── security.go
│ │ ├── etc/security.yaml
│ │ ├── internal/
│ │ │ ├── logic/
│ │ │ │ ├── encryption/
│ │ │ │ │ ├── encryptlogic.go
│ │ │ │ │ └── decryptlogic.go
│ │ │ │ ├── whitelist/
│ │ │ │ │ ├── checkwhitelistlogic.go
│ │ │ │ │ └── addwhitelistlogic.go
│ │ │ │ └── secret/
│ │ │ │ ├── generatesecretlogic.go
│ │ │ │ └── validatesecretlogic.go
│ │ │ ├── server/securityserver.go
│ │ │ ├── svc/servicecontext.go
│ │ │ └── model/
│ │ │ ├── whitelistmodel.go # IP白名单表
│ │ │ └── secretkeymodel.go # 密钥表
│ │ ├── pb/
│ │ ├── Dockerfile
│ │ └── k8s/deployment.yaml
│ │
│ ├── billing/ # 💰 计费域RPC服务
│ │ ├── billing.proto
│ │ ├── billing.go
│ │ ├── etc/billing.yaml
│ │ ├── internal/
│ │ │ ├── logic/
│ │ │ │ ├── wallet/
│ │ │ │ │ ├── getbalancelogic.go
│ │ │ │ │ ├── rechargelogic.go
│ │ │ │ │ └── freezelogic.go
│ │ │ │ ├── charge/
│ │ │ │ │ ├── chargelogic.go # 扣费逻辑
│ │ │ │ │ ├── refundlogic.go # 退费逻辑
│ │ │ │ │ └── calculatelogic.go # 计费计算
│ │ │ │ └── order/
│ │ │ │ ├── createorderlogic.go
│ │ │ │ └── queryorderlogic.go
│ │ │ ├── server/billingserver.go
│ │ │ ├── svc/servicecontext.go
│ │ │ └── model/
│ │ │ ├── walletmodel.go # 钱包表
│ │ │ ├── transactionmodel.go # 交易记录表
│ │ │ └── ordermodel.go # 订单表
│ │ ├── pb/
│ │ ├── Dockerfile
│ │ └── k8s/deployment.yaml
│ │
│ ├── product/ # 🛍️ 产品域RPC服务
│ │ ├── product.proto
│ │ ├── product.go
│ │ ├── etc/product.yaml
│ │ ├── internal/
│ │ │ ├── logic/
│ │ │ │ ├── product/
│ │ │ │ │ ├── createproductlogic.go
│ │ │ │ │ ├── getproductlogic.go
│ │ │ │ │ └── updateproductlogic.go
│ │ │ │ ├── access/
│ │ │ │ │ ├── checkproductaccesslogic.go
│ │ │ │ │ └── grantaccesslogic.go
│ │ │ │ └── subscription/
│ │ │ │ ├── subscribelogic.go
│ │ │ │ └── unsubscribelogic.go
│ │ │ ├── server/productserver.go
│ │ │ ├── svc/servicecontext.go
│ │ │ └── model/
│ │ │ ├── productmodel.go # 产品表
│ │ │ ├── userproductmodel.go # 用户产品关系表
│ │ │ └── productaccessmodel.go # 产品权限表
│ │ ├── pb/
│ │ ├── Dockerfile
│ │ └── k8s/deployment.yaml
│ │
│ └── audit/ # 📝 审计域RPC服务
│ ├── audit.proto
│ ├── audit.go
│ ├── etc/audit.yaml
│ ├── internal/
│ │ ├── logic/
│ │ │ ├── apilog/
│ │ │ │ ├── recordapiloglogic.go
│ │ │ │ └── queryapiloglogic.go
│ │ │ ├── operation/
│ │ │ │ ├── recordoperationlogic.go
│ │ │ │ └── queryoperationlogic.go
│ │ │ └── statistics/
│ │ │ ├── apistatslogic.go
│ │ │ └── usagestatslogic.go
│ │ ├── server/auditserver.go
│ │ ├── svc/servicecontext.go
│ │ └── model/
│ │ ├── apilogmodel.go # API调用日志表
│ │ ├── operationlogmodel.go # 操作日志表
│ │ └── statisticsmodel.go # 统计数据表
│ ├── pb/
│ ├── Dockerfile
│ └── k8s/deployment.yaml
├── mqs/ # 📮 消息队列服务
│ ├── consumer/ # 消息消费者
│ │ ├── audit/ # 审计消息消费
│ │ │ ├── auditconsumer.go
│ │ │ └── etc/auditconsumer.yaml
│ │ ├── billing/ # 计费消息消费
│ │ │ ├── billingconsumer.go
│ │ │ └── etc/billingconsumer.yaml
│ │ └── notification/ # 通知消息消费
│ │ ├── notificationconsumer.go
│ │ └── etc/notificationconsumer.yaml
│ └── producer/ # 消息生产者 (在各RPC服务中)
├── shared/ # 🔧 共享库
│ ├── pkg/
│ │ ├── crypto/ # 加密工具
│ │ │ ├── aes.go
│ │ │ └── rsa.go
│ │ ├── jwt/ # JWT工具
│ │ │ └── jwt.go
│ │ ├── response/ # 统一响应格式
│ │ │ └── response.go
│ │ ├── errors/ # 错误定义
│ │ │ └── errors.go
│ │ ├── utils/ # 工具函数
│ │ │ ├── strings.go
│ │ │ └── time.go
│ │ └── middleware/ # 公共中间件
│ │ ├── recovery.go
│ │ └── prometheus.go
│ ├── model/ # 公共数据模型
│ │ ├── common.go # 通用字段
│ │ └── pagination.go # 分页模型
│ └── proto/ # 公共protobuf定义
│ └── common.proto
├── deploy/ # 🚀 部署相关
│ ├── docker/
│ │ ├── docker-compose.dev.yml # 开发环境
│ │ ├── docker-compose.prod.yml # 生产环境
│ │ └── nginx/ # Nginx配置
│ │ └── nginx.conf
│ ├── k8s/ # Kubernetes部署
│ │ ├── namespace.yaml
│ │ ├── configmap.yaml
│ │ ├── mysql.yaml
│ │ ├── redis.yaml
│ │ ├── kafka.yaml
│ │ ├── etcd.yaml
│ │ └── ingress.yaml
│ ├── terraform/ # 基础设施即代码
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── scripts/ # 部署脚本
│ ├── build.sh # 构建脚本
│ ├── deploy.sh # 部署脚本
│ └── migrate.sh # 数据库迁移脚本
├── tools/ # 🛠️ 开发工具
│ ├── goctl/ # go-zero代码生成工具配置
│ │ ├── api.tpl # API模板
│ │ └── rpc.tpl # RPC模板
│ ├── migrate/ # 数据库迁移工具
│ │ ├── migrations/
│ │ │ ├── 001_create_users.sql
│ │ │ ├── 002_create_enterprises.sql
│ │ │ ├── 003_create_products.sql
│ │ │ ├── 004_create_wallets.sql
│ │ │ └── 005_create_audit_logs.sql
│ │ └── migrate.go
│ └── generate/ # 代码生成脚本
│ ├── gen-api.sh # 生成API代码
│ ├── gen-rpc.sh # 生成RPC代码
│ └── gen-model.sh # 生成Model代码
├── docs/ # 📚 项目文档
│ ├── api/ # API文档
│ │ ├── gateway.md
│ │ └── admin.md
│ ├── rpc/ # RPC服务文档
│ │ ├── user.md
│ │ ├── data.md
│ │ └── billing.md
│ ├── database/ # 数据库设计文档
│ │ ├── schema.md
│ │ └── erd.md
│ └── deployment/ # 部署文档
│ ├── k8s.md
│ └── docker.md
├── .github/ # 🔄 CI/CD
│ └── workflows/
│ ├── api-gateway.yml
│ ├── user-rpc.yml
│ ├── data-rpc.yml
│ └── deploy.yml
├── go.work # Go工作区配置
├── Makefile # 统一构建脚本
├── README.md # 项目说明
└── .gitignore # Git忽略文件
```
## 🎯 服务分类与职责
### HTTP 服务 (对外接口)
- **gateway**: 唯一 HTTP 入口,负责路由和协调
- **admin**: 管理后台,企业管理、产品管理、审核等
### RPC 服务 (内部通信)
- **user**: 用户、企业、认证管理
- **data**: 数据服务协调器 + 原有业务逻辑
- **security**: 加密解密、白名单、密钥管理
- **billing**: 钱包、计费、订单管理
- **product**: 产品、权限、订阅管理
- **audit**: 日志记录、统计分析
### MQS 服务 (异步处理)
- **audit-consumer**: 异步处理审计日志
- **billing-consumer**: 异步处理计费通知
- **notification-consumer**: 异步处理消息通知
## 💾 数据库设计
### 按域分库
```yaml
databases:
user_db: # 用户域数据库
tables:
- users
- enterprises
- user_tokens
security_db: # 安全域数据库
tables:
- ip_whitelist
- secret_keys
- encrypt_records
billing_db: # 计费域数据库
tables:
- wallets
- transactions
- orders
- billing_records
product_db: # 产品域数据库
tables:
- products
- user_products
- product_access
audit_db: # 审计域数据库
tables:
- api_logs
- operation_logs
- statistics
```
接下来我会详细说明每个部分的具体实现...

229
校验体系设计优化.md Normal file
View File

@@ -0,0 +1,229 @@
# 校验体系设计优化
## 1. 校验层次重新设计
### Handler 层client-api
```go
// types.go - 只做格式校验
type GetProductListReq struct {
CategoryId int64 `form:"category_id" validate:"min=1"` // 格式校验必须大于0
PageNum int64 `form:"page_num" validate:"min=1,max=1000"` // 格式校验:分页范围
PageSize int64 `form:"page_size" validate:"min=1,max=100"` // 格式校验:每页数量
Keyword string `form:"keyword" validate:"max=50"` // 格式校验:关键词长度
}
// handler.go - 只做参数绑定和格式校验
func (l *GetProductListHandler) GetProductList(w http.ResponseWriter, r *http.Request) {
var req types.GetProductListReq
// 1. 参数绑定
if err := httpx.Parse(r, &req); err != nil {
response.ParamErrorResponse(w, err)
return
}
// 2. 格式校验(只校验格式,不校验业务)
if err := validator.Validate(&req); err != nil {
response.ParamErrorResponse(w, err)
return
}
// 3. 调用Logic层
resp, err := l.svcCtx.GetProductListLogic(l.ctx, &req)
// ...
}
```
### RPC 层product-rpc
```go
// product.proto - RPC接口定义
message GetProductListReq {
int64 category_id = 1;
int64 page_num = 2;
int64 page_size = 3;
string keyword = 4;
int64 user_id = 5; // 业务相关用户ID
string user_role = 6; // 业务相关:用户角色
}
// logic.go - 业务校验
func (l *GetProductListLogic) GetProductList(req *product.GetProductListReq) (*product.GetProductListResp, error) {
// 1. 业务校验(在相关业务域内)
if err := l.validateBusinessRules(req); err != nil {
return nil, err
}
// 2. 业务逻辑处理
// ...
}
func (l *GetProductListLogic) validateBusinessRules(req *product.GetProductListReq) error {
// 业务规则校验:用户是否有权限查看该分类
if !l.svcCtx.UserModel.HasCategoryPermission(req.UserId, req.CategoryId) {
return errors.New("用户无权限查看该分类")
}
// 业务规则校验:分类是否存在且启用
category, err := l.svcCtx.CategoryModel.FindOne(req.CategoryId)
if err != nil || category.Status != 1 {
return errors.New("分类不存在或已禁用")
}
return nil
}
```
## 2. 参数传递和转换
### Logic 层的作用
```go
// client/internal/logic/product/getproductlistlogic.go
func (l *GetProductListLogic) GetProductList(req *types.GetProductListReq) (*types.GetProductListResp, error) {
// 1. 获取用户信息从JWT中解析
userInfo := l.ctx.Value("userInfo").(auth.UserInfo)
// 2. 转换为RPC请求添加业务上下文
rpcReq := &product.GetProductListReq{
CategoryId: req.CategoryId,
PageNum: req.PageNum,
PageSize: req.PageSize,
Keyword: req.Keyword,
UserId: userInfo.UserId, // 添加业务上下文
UserRole: userInfo.Role, // 添加业务上下文
}
// 3. 调用RPC服务
rpcResp, err := l.svcCtx.ProductRpc.GetProductList(l.ctx, rpcReq)
if err != nil {
return nil, err
}
// 4. 转换响应格式
return &types.GetProductListResp{
List: convertToApiFormat(rpcResp.List),
Total: rpcResp.Total,
}, nil
}
```
## 3. 校验规则组织优化
### 目录结构调整
```
shared/
├── validator/ # 通用格式校验器
│ ├── validator.go # 校验器初始化
│ ├── rules.go # 通用校验规则
│ └── messages.go # 错误消息
├── errcode/ # 错误码定义
│ ├── api_errors.go # API层错误
│ └── business_errors.go # 业务层错误
└── utils/ # 工具函数
└── converter.go # 数据转换
domains/
├── product/
│ ├── rpc/
│ │ └── internal/
│ │ └── logic/
│ │ └── validator/ # 产品域业务校验
│ │ ├── product_validator.go
│ │ └── category_validator.go
├── user/
│ └── rpc/
│ └── internal/
│ └── logic/
│ └── validator/ # 用户域业务校验
│ ├── user_validator.go
│ └── auth_validator.go
```
### 业务校验器实现
```go
// domains/product/rpc/internal/logic/validator/product_validator.go
package validator
import (
"context"
"tianyuan/domains/product/rpc/internal/svc"
"tianyuan/domains/product/rpc/product"
)
type ProductValidator struct {
svcCtx *svc.ServiceContext
}
func NewProductValidator(svcCtx *svc.ServiceContext) *ProductValidator {
return &ProductValidator{
svcCtx: svcCtx,
}
}
// 校验用户是否有权限查看产品列表
func (v *ProductValidator) ValidateGetProductListPermission(ctx context.Context, req *product.GetProductListReq) error {
// 检查用户权限
hasPermission, err := v.svcCtx.UserRpc.CheckPermission(ctx, &user.CheckPermissionReq{
UserId: req.UserId,
Permission: "product:list",
})
if err != nil {
return err
}
if !hasPermission.HasPermission {
return errors.New("用户无权限查看产品列表")
}
return nil
}
// 校验分类相关业务规则
func (v *ProductValidator) ValidateCategoryAccess(ctx context.Context, req *product.GetProductListReq) error {
// 检查分类是否存在且用户有访问权限
// ...
return nil
}
```
## 4. 关于 httpx.Parse 的说明
`httpx.Parse` 是 go-zero 提供的参数绑定函数,它会:
1. **自动绑定参数**:根据 struct tag 从 HTTP 请求中解析参数
2. **支持多种来源**query 参数、form 数据、JSON body 等
3. **类型转换**:自动进行基础类型转换
```go
// 示例httpx.Parse 的使用
type GetProductListReq struct {
CategoryId int64 `form:"category_id"` // 从form或query中获取
PageNum int64 `form:"page_num"`
PageSize int64 `form:"page_size"`
Keyword string `form:"keyword"`
}
func Handler(w http.ResponseWriter, r *http.Request) {
var req GetProductListReq
// 这一行会自动从 r *http.Request 中解析参数到 req 结构体
if err := httpx.Parse(r, &req); err != nil {
// 参数解析失败
return
}
// 此时 req 已经包含了解析后的参数值
}
```
## 5. 最佳实践总结
1. **Handler 层**:只做格式校验,使用 validator tag
2. **Logic 层**:参数转换和聚合,添加业务上下文
3. **RPC 层**:业务规则校验,放在对应的业务域内
4. **校验器分离**:格式校验器放 shared业务校验器放各自域内
5. **参数绑定**:使用 go-zero 的 httpx.Parse 自动绑定

View File

@@ -0,0 +1,414 @@
# 网关域架构与跨域调用实现详解
## 🌐 网关域的职责与架构
### 网关域的核心职责
```
网关域 (Gateway Domain) = API网关 + 路由管理 + 协议转换
```
**主要功能:**
1. **统一入口** - 所有外部请求的唯一入口点
2. **路由分发** - 根据路径将请求分发到对应的域服务
3. **协议转换** - HTTP ↔ gRPC 转换
4. **横切关注点** - 认证、限流、监控、日志
5. **服务发现** - 动态发现后端服务
### 网关域架构图
```
┌─────────────────────────────────────────────────────────────┐
│ Gateway Domain │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ Gateway Service ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ HTTP │ │ Router │ │ gRPC │ ││
│ │ │ Handlers │ │ Engine │ │ Clients │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────┘ ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ Auth │ │ Rate Limit │ │ Monitor │ ││
│ │ │ Middleware │ │ Middleware │ │ Middleware │ ││
│ │ └─────────────┘ └─────────────┘ └─────────────┘ ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Domain │ │ Data Domain │ │ Billing Domain │
│ (User Service) │ │ (Data Service) │ │(Billing Service)│
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
## 🏗️ 网关域的目录结构
```
domains/gateway-domain/
└── gateway-service/
├── cmd/
│ └── server/
│ └── main.go # 网关服务启动入口
├── api/
│ └── http/
│ └── gateway.api # 网关API定义
├── internal/
│ ├── application/ # 应用层
│ │ └── service/
│ │ └── gateway_orchestrator.go # 🔥 核心协调器
│ │
│ ├── domain/ # 领域层
│ │ ├── entity/
│ │ │ ├── route.go # 路由实体
│ │ │ └── service_endpoint.go # 服务端点
│ │ └── service/
│ │ ├── router_service.go # 路由服务
│ │ └── discovery_service.go # 服务发现
│ │
│ ├── infrastructure/ # 基础设施层
│ │ ├── client/ # gRPC客户端
│ │ │ ├── user_client.go
│ │ │ ├── data_client.go
│ │ │ ├── security_client.go
│ │ │ ├── billing_client.go
│ │ │ └── product_client.go
│ │ ├── discovery/ # 服务发现
│ │ │ └── etcd_discovery.go
│ │ └── config/
│ │ └── service_config.go # 服务配置
│ │
│ └── interfaces/ # 接口适配器层
│ ├── http/ # HTTP处理器
│ │ ├── data_handler.go # 数据服务API处理
│ │ ├── user_handler.go # 用户服务API处理
│ │ └── billing_handler.go # 计费服务API处理
│ ├── middleware/ # 中间件
│ │ ├── auth_middleware.go # 认证中间件
│ │ ├── rate_limit_middleware.go # 限流中间件
│ │ ├── audit_middleware.go # 审计中间件
│ │ └── cors_middleware.go # CORS中间件
│ └── router/
│ └── gateway_router.go # 路由配置
├── configs/
│ ├── gateway.yaml # 网关配置
│ └── routes.yaml # 路由配置
└── deployments/
├── Dockerfile
└── k8s/
```
## 🎯 核心实现:跨域调用 Logic 在哪里
### 1. 网关层的协调逻辑 (Gateway Orchestrator)
```go
// domains/gateway-domain/gateway-service/internal/application/service/gateway_orchestrator.go
type GatewayOrchestrator struct {
// gRPC客户端 - 调用各个域服务
userClient user.UserServiceClient
dataClient data.DataServiceClient
securityClient security.SecurityServiceClient
billingClient billing.BillingServiceClient
productClient product.ProductServiceClient
auditClient audit.AuditServiceClient
// 网关自身的服务
routerService *RouterService
discoveryService *DiscoveryService
}
// 🔥 这里实现跨域调用的核心逻辑
func (g *GatewayOrchestrator) ProcessAPIRequest(ctx context.Context, req *APIRequest) (*APIResponse, error) {
// 1. 解析路由 - 确定目标域
route, err := g.routerService.ResolveRoute(req.Path)
if err != nil {
return nil, err
}
// 2. 根据不同的业务场景,实现不同的协调逻辑
switch route.Domain {
case "data":
return g.handleDataDomainRequest(ctx, req)
case "user":
return g.handleUserDomainRequest(ctx, req)
case "billing":
return g.handleBillingDomainRequest(ctx, req)
default:
return nil, errors.New("unknown domain")
}
}
// 🔥 数据域请求的协调逻辑 (你之前问的API调用流程)
func (g *GatewayOrchestrator) handleDataDomainRequest(ctx context.Context, req *APIRequest) (*APIResponse, error) {
// 注意这里只做最基础的协调复杂的业务逻辑在Data Domain内部
// 1. 前置验证 (网关层职责)
if err := g.validateBasicRequest(req); err != nil {
return nil, err
}
// 2. 认证检查 (网关层职责)
userInfo, err := g.authenticateUser(ctx, req.Headers.Authorization)
if err != nil {
return nil, err
}
// 3. 调用数据域服务 (核心业务逻辑在数据域内部)
dataReq := &data.ProcessAPIRequestRequest{
UserId: userInfo.UserId,
EnterpriseId: userInfo.EnterpriseId,
ApiPath: req.Path,
Parameters: req.Parameters,
ClientIp: req.ClientIP,
}
// 🚨 重点复杂的跨域协调逻辑在数据域的DataOrchestrator中实现
dataResp, err := g.dataClient.ProcessAPIRequest(ctx, dataReq)
if err != nil {
return nil, err
}
// 4. 响应转换 (网关层职责)
return g.convertToHTTPResponse(dataResp), nil
}
// 简单的认证逻辑 (复杂认证在User Domain)
func (g *GatewayOrchestrator) authenticateUser(ctx context.Context, authHeader string) (*UserInfo, error) {
// 调用用户域进行认证
authReq := &user.AuthenticateRequest{
Token: authHeader,
}
authResp, err := g.userClient.Authenticate(ctx, authReq)
if err != nil {
return nil, err
}
return &UserInfo{
UserId: authResp.UserId,
EnterpriseId: authResp.EnterpriseId,
}, nil
}
```
### 2. 数据域的协调逻辑 (Data Orchestrator)
```go
// domains/data-domain/data-service/internal/application/service/data_orchestrator.go
type DataOrchestrator struct {
// 数据域内部的业务服务
riskService *RiskAssessmentService // 原FLXG
creditService *CreditCheckService // 原JRZQ
companyService *CompanyInfoService // 原QYGL
queryService *DataQueryService // 原IVYZ
// 其他域的gRPC客户端
securityClient SecurityServiceClient // 安全域
userClient UserServiceClient // 用户域
productClient ProductServiceClient // 产品域
billingClient BillingServiceClient // 计费域
auditClient AuditServiceClient // 审计域
}
// 🔥 这里实现你问的那个复杂API调用流程的核心协调逻辑
func (d *DataOrchestrator) ProcessAPIRequest(ctx context.Context, req *ProcessAPIRequestRequest) (*ProcessAPIResponseResponse, error) {
// === 第一阶段:安全验证 ===
// 1. IP白名单验证 (调用安全域)
whitelistReq := &security.CheckWhitelistRequest{
EnterpriseId: req.EnterpriseId,
ClientIp: req.ClientIp,
}
if _, err := d.securityClient.CheckWhitelist(ctx, whitelistReq); err != nil {
return nil, fmt.Errorf("IP白名单验证失败: %w", err)
}
// 2. 企业ID验证 (调用用户域)
enterpriseReq := &user.ValidateEnterpriseRequest{
UserId: req.UserId,
EnterpriseId: req.EnterpriseId,
}
enterpriseResp, err := d.userClient.ValidateEnterprise(ctx, enterpriseReq)
if err != nil {
return nil, fmt.Errorf("企业验证失败: %w", err)
}
// 3. 密钥解密 (调用安全域)
decryptReq := &security.DecryptSecretRequest{
EnterpriseId: req.EnterpriseId,
EncryptedKey: enterpriseResp.EncryptedSecretKey,
}
decryptResp, err := d.securityClient.DecryptSecret(ctx, decryptReq)
if err != nil {
return nil, fmt.Errorf("密钥解密失败: %w", err)
}
// === 第二阶段:权限与产品验证 ===
// 4. 产品权限检查 (调用产品域)
productReq := &product.CheckProductAccessRequest{
UserId: req.UserId,
ApiPath: req.ApiPath,
SecretKey: decryptResp.SecretKey,
}
productResp, err := d.productClient.CheckProductAccess(ctx, productReq)
if err != nil {
return nil, fmt.Errorf("产品权限检查失败: %w", err)
}
// 5. 余额检查 (调用计费域)
balanceReq := &billing.CheckBalanceRequest{
UserId: req.UserId,
ProductCode: productResp.ProductCode,
ApiPath: req.ApiPath,
}
balanceResp, err := d.billingClient.CheckBalance(ctx, balanceReq)
if err != nil {
return nil, fmt.Errorf("余额检查失败: %w", err)
}
// === 第三阶段:业务处理 ===
// 6. 根据API路径选择对应的业务服务 (数据域内部逻辑)
var businessResult *BusinessResult
switch {
case strings.Contains(req.ApiPath, "risk-assessment"):
businessResult, err = d.riskService.ProcessRequest(ctx, req.Parameters)
case strings.Contains(req.ApiPath, "credit-check"):
businessResult, err = d.creditService.ProcessRequest(ctx, req.Parameters)
case strings.Contains(req.ApiPath, "company-info"):
businessResult, err = d.companyService.ProcessRequest(ctx, req.Parameters)
case strings.Contains(req.ApiPath, "data-query"):
businessResult, err = d.queryService.ProcessRequest(ctx, req.Parameters)
default:
return nil, errors.New("不支持的API路径")
}
if err != nil {
return nil, fmt.Errorf("业务处理失败: %w", err)
}
// === 第四阶段:计费与审计 ===
// 7. 计费扣款 (调用计费域)
chargeReq := &billing.ChargeRequest{
UserId: req.UserId,
ProductCode: productResp.ProductCode,
ApiPath: req.ApiPath,
Amount: balanceResp.RequiredAmount,
RequestId: generateRequestId(),
}
chargeResp, err := d.billingClient.Charge(ctx, chargeReq)
if err != nil {
return nil, fmt.Errorf("计费失败: %w", err)
}
// 8. 记录审计日志 (调用审计域)
auditReq := &audit.RecordAPICallRequest{
UserId: req.UserId,
EnterpriseId: req.EnterpriseId,
ApiPath: req.ApiPath,
ClientIp: req.ClientIp,
RequestId: chargeResp.TransactionId,
Result: "success",
ResponseTime: time.Since(startTime).Milliseconds(),
}
go d.auditClient.RecordAPICall(context.Background(), auditReq) // 异步记录
// 9. 返回最终结果
return &ProcessAPIResponseResponse{
Success: true,
Data: businessResult.Data,
TransactionId: chargeResp.TransactionId,
RemainingBalance: chargeResp.RemainingBalance,
}, nil
}
```
### 3. HTTP 处理器 (接口适配器层)
```go
// domains/gateway-domain/gateway-service/internal/interfaces/http/data_handler.go
type DataHandler struct {
orchestrator *GatewayOrchestrator
}
// HTTP入口点
func (h *DataHandler) HandleDataAPI(c *gin.Context) {
// 1. HTTP请求转换
req := &APIRequest{
Path: c.Request.URL.Path,
Method: c.Request.Method,
Parameters: extractParameters(c),
Headers: extractHeaders(c),
ClientIP: c.ClientIP(),
}
// 2. 调用网关协调器
resp, err := h.orchestrator.ProcessAPIRequest(c.Request.Context(), req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// 3. HTTP响应
c.JSON(http.StatusOK, resp)
}
```
## 🌊 完整的调用流程
```
1. 客户端 HTTP请求
2. 网关域 (Gateway Domain)
├── HTTP Handler (接口适配器层)
├── Gateway Orchestrator (应用层)
│ ├── 基础验证
│ ├── 用户认证 → User Domain
│ └── 路由到目标域
└── 调用 Data Domain
3. 数据域 (Data Domain)
└── Data Orchestrator (应用层) 🔥 核心协调逻辑
├── 安全验证 → Security Domain
├── 企业验证 → User Domain
├── 权限检查 → Product Domain
├── 余额检查 → Billing Domain
├── 业务处理 (内部服务)
├── 计费扣款 → Billing Domain
└── 审计记录 → Audit Domain
```
## 🎯 总结Logic 实现的分层
### 1. **网关域的职责** (简单协调)
- HTTP 协议处理
- 路由分发
- 基础认证
- 中间件处理 (限流、CORS 等)
### 2. **数据域的职责** (复杂协调) 🔥
- **这里是你问的那个复杂 API 调用流程的主要实现位置**
- 跨域服务协调
- 业务流程编排
- 事务一致性保证
### 3. **其他域的职责** (专业服务)
- 各自领域的专业逻辑
- 对外提供 gRPC 接口
**关键点:网关域负责"谁来处理",数据域负责"怎么处理"**

766
网关管理详解.md Normal file
View File

@@ -0,0 +1,766 @@
# 微服务网关管理详解
## 🚪 什么是 API 网关?
API 网关就像一个**智能门卫**,站在所有微服务的前面:
```
客户端请求 → API网关 → 路由到具体的微服务
↑ ↑ ↑
用户App 统一入口 用户服务
管理后台 ↓ 支付服务
第三方系统 认证授权 产品服务
限流控制 数据服务
日志记录
```
## 🎯 网关的核心功能
### 1. 请求路由 (Request Routing)
```go
// 路由配置示例
type Route struct {
Path string `yaml:"path"`
Method string `yaml:"method"`
Service string `yaml:"service"`
Middlewares []string `yaml:"middlewares"`
Headers map[string]string `yaml:"headers"`
}
var routes = []Route{
{
Path: "/api/v1/users/*",
Method: "*",
Service: "user-service",
Middlewares: []string{"auth", "rate-limit"},
},
{
Path: "/api/v1/payments/*",
Method: "*",
Service: "payment-service",
Middlewares: []string{"auth", "encrypt"},
},
{
Path: "/api/v1/products/*",
Method: "*",
Service: "product-service",
Middlewares: []string{"auth", "cache"},
},
}
```
### 2. 负载均衡 (Load Balancing)
```go
// 负载均衡器
type LoadBalancer interface {
Next() *ServiceInstance
}
// 轮询负载均衡
type RoundRobinBalancer struct {
instances []*ServiceInstance
current int
mutex sync.Mutex
}
func (rb *RoundRobinBalancer) Next() *ServiceInstance {
rb.mutex.Lock()
defer rb.mutex.Unlock()
if len(rb.instances) == 0 {
return nil
}
instance := rb.instances[rb.current]
rb.current = (rb.current + 1) % len(rb.instances)
return instance
}
// 加权轮询
type WeightedRoundRobinBalancer struct {
instances []*WeightedInstance
total int
current int
}
type WeightedInstance struct {
Instance *ServiceInstance
Weight int
Current int
}
```
### 3. 服务发现 (Service Discovery)
```go
// 服务发现接口
type ServiceDiscovery interface {
Register(service *ServiceInstance) error
Deregister(serviceID string) error
Discover(serviceName string) ([]*ServiceInstance, error)
Watch(serviceName string) <-chan []*ServiceInstance
}
// Consul 服务发现实现
type ConsulDiscovery struct {
client *consul.Client
}
func (cd *ConsulDiscovery) Discover(serviceName string) ([]*ServiceInstance, error) {
services, _, err := cd.client.Health().Service(serviceName, "", true, nil)
if err != nil {
return nil, err
}
var instances []*ServiceInstance
for _, service := range services {
instances = append(instances, &ServiceInstance{
ID: service.Service.ID,
Name: service.Service.Service,
Address: service.Service.Address,
Port: service.Service.Port,
Meta: service.Service.Meta,
})
}
return instances, nil
}
```
## 🛡️ 网关中间件系统
### 1. 认证中间件
```go
// 认证中间件
func AuthMiddleware(jwtSecret string) gin.HandlerFunc {
return func(c *gin.Context) {
// 1. 获取 Token
token := extractToken(c)
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "缺少认证令牌"})
c.Abort()
return
}
// 2. 验证 Token
claims, err := validateJWT(token, jwtSecret)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "无效的认证令牌"})
c.Abort()
return
}
// 3. 设置用户上下文
c.Set("user_id", claims.UserID)
c.Set("username", claims.Username)
c.Set("roles", claims.Roles)
c.Next()
}
}
func extractToken(c *gin.Context) string {
// 从 Header 获取
authHeader := c.GetHeader("Authorization")
if authHeader != "" && strings.HasPrefix(authHeader, "Bearer ") {
return strings.TrimPrefix(authHeader, "Bearer ")
}
// 从 Query 参数获取
return c.Query("token")
}
```
### 2. 限流中间件
```go
// 限流中间件
type RateLimiter struct {
redis *redis.Client
window time.Duration
limit int
}
func NewRateLimiter(redis *redis.Client, window time.Duration, limit int) *RateLimiter {
return &RateLimiter{
redis: redis,
window: window,
limit: limit,
}
}
func (rl *RateLimiter) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 1. 获取客户端标识
clientID := getClientID(c)
// 2. 检查限流
allowed, err := rl.isAllowed(clientID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "限流检查失败"})
c.Abort()
return
}
if !allowed {
c.JSON(http.StatusTooManyRequests, gin.H{
"error": "请求过于频繁,请稍后再试",
"retry_after": int(rl.window.Seconds()),
})
c.Abort()
return
}
c.Next()
}
}
func (rl *RateLimiter) isAllowed(clientID string) (bool, error) {
key := fmt.Sprintf("rate_limit:%s", clientID)
// 使用滑动窗口算法
now := time.Now().Unix()
window := now - int64(rl.window.Seconds())
pipe := rl.redis.Pipeline()
// 删除过期的记录
pipe.ZRemRangeByScore(context.Background(), key, "0", fmt.Sprintf("%d", window))
// 获取当前窗口内的请求数
countCmd := pipe.ZCard(context.Background(), key)
// 添加当前请求
pipe.ZAdd(context.Background(), key, &redis.Z{
Score: float64(now),
Member: fmt.Sprintf("%d", now),
})
// 设置过期时间
pipe.Expire(context.Background(), key, rl.window)
_, err := pipe.Exec(context.Background())
if err != nil {
return false, err
}
count := countCmd.Val()
return count < int64(rl.limit), nil
}
func getClientID(c *gin.Context) string {
// 优先使用用户ID
if userID := c.GetString("user_id"); userID != "" {
return "user:" + userID
}
// 其次使用API Key
if apiKey := c.GetHeader("X-API-Key"); apiKey != "" {
return "api:" + apiKey
}
// 最后使用IP地址
return "ip:" + c.ClientIP()
}
```
### 3. 缓存中间件
```go
// 缓存中间件
type CacheMiddleware struct {
redis *redis.Client
ttl time.Duration
}
func NewCacheMiddleware(redis *redis.Client, ttl time.Duration) *CacheMiddleware {
return &CacheMiddleware{
redis: redis,
ttl: ttl,
}
}
func (cm *CacheMiddleware) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 只缓存 GET 请求
if c.Request.Method != http.MethodGet {
c.Next()
return
}
// 生成缓存键
cacheKey := generateCacheKey(c)
// 尝试从缓存获取
cached, err := cm.redis.Get(context.Background(), cacheKey).Result()
if err == nil {
var response CachedResponse
if json.Unmarshal([]byte(cached), &response) == nil {
// 设置响应头
for key, value := range response.Headers {
c.Header(key, value)
}
c.Data(response.Status, response.ContentType, response.Body)
c.Abort()
return
}
}
// 包装 ResponseWriter 来捕获响应
crw := &cachedResponseWriter{
ResponseWriter: c.Writer,
body: &bytes.Buffer{},
headers: make(map[string]string),
}
c.Writer = crw
c.Next()
// 缓存响应
if crw.status >= 200 && crw.status < 300 {
response := CachedResponse{
Status: crw.status,
Headers: crw.headers,
ContentType: crw.Header().Get("Content-Type"),
Body: crw.body.Bytes(),
}
if data, err := json.Marshal(response); err == nil {
cm.redis.Set(context.Background(), cacheKey, data, cm.ttl)
}
}
}
}
type CachedResponse struct {
Status int `json:"status"`
Headers map[string]string `json:"headers"`
ContentType string `json:"content_type"`
Body []byte `json:"body"`
}
type cachedResponseWriter struct {
gin.ResponseWriter
body *bytes.Buffer
headers map[string]string
status int
}
func (crw *cachedResponseWriter) Write(data []byte) (int, error) {
crw.body.Write(data)
return crw.ResponseWriter.Write(data)
}
func (crw *cachedResponseWriter) WriteHeader(status int) {
crw.status = status
crw.ResponseWriter.WriteHeader(status)
}
```
## 🔧 网关配置管理
### 1. 动态配置
```yaml
# gateway.yaml
gateway:
port: 8080
timeout: 30s
services:
user-service:
discovery: consul
load_balancer: round_robin
health_check:
enabled: true
interval: 10s
timeout: 3s
path: /health
circuit_breaker:
enabled: true
failure_threshold: 5
timeout: 60s
payment-service:
discovery: consul
load_balancer: weighted_round_robin
weights:
- instance: payment-service-1
weight: 3
- instance: payment-service-2
weight: 1
middlewares:
auth:
jwt_secret: ${JWT_SECRET}
excluded_paths:
- /api/v1/auth/login
- /api/v1/auth/register
- /health
rate_limit:
default:
window: 60s
limit: 100
user_limits:
premium_user:
window: 60s
limit: 1000
basic_user:
window: 60s
limit: 100
cache:
ttl: 300s
excluded_paths:
- /api/v1/users/me
- /api/v1/payments/*
routes:
- path: /api/v1/users/*
service: user-service
middlewares: [auth, rate_limit]
- path: /api/v1/payments/*
service: payment-service
middlewares: [auth, rate_limit]
- path: /api/v1/products/*
service: product-service
middlewares: [auth, rate_limit, cache]
```
### 2. 配置热更新
```go
// 配置管理器
type ConfigManager struct {
config *GatewayConfig
mutex sync.RWMutex
watchers []chan *GatewayConfig
}
func NewConfigManager() *ConfigManager {
return &ConfigManager{
watchers: make([]chan *GatewayConfig, 0),
}
}
func (cm *ConfigManager) LoadConfig(path string) error {
cm.mutex.Lock()
defer cm.mutex.Unlock()
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
var config GatewayConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return err
}
cm.config = &config
// 通知所有监听者
for _, watcher := range cm.watchers {
select {
case watcher <- &config:
default:
// 非阻塞发送
}
}
return nil
}
func (cm *ConfigManager) Watch() <-chan *GatewayConfig {
cm.mutex.Lock()
defer cm.mutex.Unlock()
watcher := make(chan *GatewayConfig, 1)
cm.watchers = append(cm.watchers, watcher)
// 立即发送当前配置
if cm.config != nil {
watcher <- cm.config
}
return watcher
}
func (cm *ConfigManager) WatchFile(path string) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
err = watcher.Add(path)
if err != nil {
log.Fatal(err)
}
for {
select {
case event := <-watcher.Events:
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("配置文件已修改,重新加载...")
time.Sleep(100 * time.Millisecond) // 避免多次触发
cm.LoadConfig(path)
}
case err := <-watcher.Errors:
log.Println("配置文件监听错误:", err)
}
}
}
```
## 📊 网关监控和可观测性
### 1. 指标收集
```go
// 指标收集
var (
requestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "gateway_requests_total",
Help: "Total number of requests",
},
[]string{"service", "method", "path", "status"},
)
requestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "gateway_request_duration_seconds",
Help: "Request duration in seconds",
Buckets: prometheus.DefBuckets,
},
[]string{"service", "method", "path"},
)
activeConnections = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "gateway_active_connections",
Help: "Number of active connections",
},
)
)
func MetricsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
activeConnections.Inc()
defer activeConnections.Dec()
c.Next()
duration := time.Since(start).Seconds()
status := strconv.Itoa(c.Writer.Status())
service := c.GetString("target_service")
requestsTotal.WithLabelValues(
service,
c.Request.Method,
c.Request.URL.Path,
status,
).Inc()
requestDuration.WithLabelValues(
service,
c.Request.Method,
c.Request.URL.Path,
).Observe(duration)
}
}
```
### 2. 链路追踪
```go
// OpenTelemetry 追踪
func TracingMiddleware(tracer trace.Tracer) gin.HandlerFunc {
return func(c *gin.Context) {
spanName := fmt.Sprintf("%s %s", c.Request.Method, c.Request.URL.Path)
ctx, span := tracer.Start(c.Request.Context(), spanName)
defer span.End()
// 设置 span 属性
span.SetAttributes(
attribute.String("http.method", c.Request.Method),
attribute.String("http.url", c.Request.URL.String()),
attribute.String("http.user_agent", c.Request.UserAgent()),
attribute.String("client.ip", c.ClientIP()),
)
// 传递上下文
c.Request = c.Request.WithContext(ctx)
c.Next()
// 设置响应属性
span.SetAttributes(
attribute.Int("http.status_code", c.Writer.Status()),
attribute.Int("http.response_size", c.Writer.Size()),
)
if c.Writer.Status() >= 400 {
span.SetStatus(codes.Error, fmt.Sprintf("HTTP %d", c.Writer.Status()))
}
}
}
```
## 🏗️ 网关架构设计
### 1. 高可用部署
```yaml
# docker-compose.yml
version: "3.8"
services:
gateway-1:
image: your-gateway:latest
ports:
- "8080:8080"
environment:
- GATEWAY_ID=gateway-1
- CONSUL_ADDRESS=consul:8500
depends_on:
- consul
- redis
gateway-2:
image: your-gateway:latest
ports:
- "8081:8080"
environment:
- GATEWAY_ID=gateway-2
- CONSUL_ADDRESS=consul:8500
depends_on:
- consul
- redis
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- gateway-1
- gateway-2
consul:
image: consul:latest
ports:
- "8500:8500"
command: consul agent -dev -client=0.0.0.0
redis:
image: redis:alpine
ports:
- "6379:6379"
```
### 2. Nginx 负载均衡配置
```nginx
# nginx.conf
upstream gateway {
server gateway-1:8080 weight=1 max_fails=3 fail_timeout=30s;
server gateway-2:8080 weight=1 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://gateway;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# 健康检查
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 健康检查端点
location /health {
access_log off;
proxy_pass http://gateway;
}
}
```
## 🚀 实施建议
### 1. 渐进式迁移
```
阶段1: 搭建基础网关
├── 基本的路由功能
├── 健康检查
└── 监控指标
阶段2: 添加安全功能
├── JWT 认证
├── API 限流
└── 请求日志
阶段3: 性能优化
├── 响应缓存
├── 连接池
└── 负载均衡
阶段4: 高级功能
├── 熔断器
├── 灰度发布
└── API 版本管理
```
### 2. 网关选型建议
**自研网关 (推荐给你)**:
```go
// 基于 gin + consul + redis 的轻量级网关
// 优势: 完全控制、定制化强、集成容易
// 适合: 中小型项目、快速迭代
```
**开源网关**:
- **Kong**: 功能丰富,插件生态好
- **Traefik**: 配置简单,支持多种后端
- **Envoy**: 性能极高,配置复杂
## 📋 总结
网关管理的核心要点:
1. **统一入口** - 所有外部请求通过网关
2. **服务路由** - 将请求路由到正确的后端服务
3. **安全认证** - 统一的身份验证和授权
4. **流量控制** - 限流、熔断、负载均衡
5. **可观测性** - 监控、日志、链路追踪
6. **高可用** - 多实例部署、健康检查
对于你的项目,建议先实现基础的路由和认证功能,然后逐步添加高级特性。

610
重构方案.md Normal file
View File

@@ -0,0 +1,610 @@
# 天远 API 服务器重构方案
## 🎯 重构目标
1. **提高代码可维护性** - 清晰的业务边界和模块划分
2. **增强系统扩展性** - 松耦合的微服务架构
3. **改善开发效率** - 标准化的开发流程和工具链
4. **提升系统可靠性** - 完善的监控、测试和部署体系
## 🏗️ 架构重构
### 1. 业务域重新设计 (DDD)
```
新的业务域架构:
┌─────────────────────────────────────────────────────────────┐
│ API Gateway │
│ (统一入口网关) │
└─────────────────────────────────────────────────────────────┘
┌──────────────┼──────────────┐
│ │ │
┌───────────────▼──┐ ┌─────────▼──────┐ ┌────▼──────────┐
│ 用户域服务 │ │ 金融域服务 │ │ 数据服务域 │
│ (User Domain) │ │(Financial Domain)│ │(Data Domain) │
├─────────────────┤ ├─────────────────┤ ├──────────────┤
│• 用户认证 │ │• 钱包管理 │ │• 风险评估 │
│• 用户信息 │ │• 支付处理 │ │• 征信查询 │
│• 企业认证 │ │• 计费结算 │ │• 企业信息 │
│• 权限管理 │ │• 财务报表 │ │• 数据查询 │
└─────────────────┘ └─────────────────┘ └──────────────┘
┌──────────────┼──────────────┐
│ │ │
┌───────────────▼──┐ ┌─────────▼──────┐ ┌────▼──────────┐
│ 产品域服务 │ │ 平台域服务 │ │ 基础设施域 │
│(Product Domain) │ │(Platform Domain)│ │(Infra Domain) │
├─────────────────┤ ├─────────────────┤ ├──────────────┤
│• 产品目录 │ │• 管理后台 │ │• 消息队列 │
│• 产品订阅 │ │• 系统监控 │ │• 缓存服务 │
│• 访问控制 │ │• 日志管理 │ │• 配置中心 │
│• 白名单管理 │ │• 运维工具 │ │• 服务发现 │
└─────────────────┘ └─────────────────┘ └──────────────┘
```
### 2. 微服务重新设计
#### 用户域 (User Domain)
```go
// 用户认证服务
user-auth-service/
├── api/
├── auth.proto # gRPC 接口定义
└── auth.api # HTTP 接口定义
├── internal/
├── domain/ # 领域模型
├── application/ # 应用服务
├── infrastructure/ # 基础设施
└── interfaces/ # 接口适配器
└── cmd/
└── main.go
// 企业认证服务
enterprise-service/
├── api/
├── internal/
└── cmd/
```
#### 金融域 (Financial Domain)
```go
// 钱包服务
wallet-service/
├── api/
├── internal/
├── domain/
├── wallet/ # 钱包聚合根
├── transaction/ # 交易实体
└── balance/ # 余额值对象
├── application/
└── infrastructure/
└── cmd/
// 支付服务
payment-service/
├── api/
├── internal/
└── cmd/
```
#### 数据服务域 (Data Service Domain)
```go
// 统一数据服务网关
data-gateway-service/
├── api/
├── internal/
├── adapters/ # 第三方数据源适配器
├── westdex/ # 西部数据源
├── baidu/ # 百度接口
└── aliyun/ # 阿里云接口
├── domain/
├── query/ # 查询服务
├── risk/ # 风险评估
└── credit/ # 征信服务
└── application/
└── cmd/
```
#### 产品域 (Product Domain)
```go
// 产品管理服务
product-service/
├── api/
├── internal/
├── domain/
├── product/ # 产品聚合根
├── subscription/ # 订阅实体
└── access/ # 访问控制
└── application/
└── cmd/
```
## 🛠️ 技术重构
### 1. 代码结构标准化
**采用六边形架构 (Hexagonal Architecture)**
```go
service/
├── api/ # 接口定义
├── grpc/ # gRPC 协议
├── http/ # HTTP 协议
└── event/ # 事件协议
├── internal/
├── domain/ # 领域层
├── entity/ # 实体
├── valueobject/ # 值对象
├── aggregate/ # 聚合根
├── repository/ # 仓储接口
└── service/ # 领域服务
├── application/ # 应用层
├── command/ # 命令处理
├── query/ # 查询处理
├── dto/ # 数据传输对象
└── service/ # 应用服务
├── infrastructure/ # 基础设施层
├── persistence/ # 持久化
├── messaging/ # 消息传递
├── external/ # 外部服务
└── config/ # 配置
└── interfaces/ # 接口适配器层
├── grpc/ # gRPC 适配器
├── http/ # HTTP 适配器
├── event/ # 事件适配器
└── cli/ # 命令行适配器
├── pkg/ # 共享包
├── test/ # 测试
├── docs/ # 文档
├── deployments/ # 部署配置
├── Dockerfile
└── go.mod
```
### 2. 技术栈升级
**推荐的技术栈**
```yaml
# 微服务框架
framework: go-zero v1.6+ 或 go-kit v0.12+
# 数据库
database:
- PostgreSQL 15+ (替代MySQL, 更好的JSON支持)
- Redis 7+ (缓存和会话)
- ClickHouse (日志和分析)
# 消息队列
messaging:
- Apache Kafka 3.0+ (事件流)
- RabbitMQ 3.11+ (任务队列)
# 服务治理
service_mesh: Istio 1.17+
service_discovery: Consul 1.15+
configuration: Consul KV 或 etcd
# 监控和可观测性
monitoring:
- Prometheus + Grafana
- Jaeger (分布式追踪)
- ELK Stack (日志)
- SkyWalking (APM)
# 安全
security:
- Vault (密钥管理)
- OPA (策略引擎)
- Keycloak (身份认证)
```
## 📊 数据层重构
### 1. 数据库重新设计
**按业务域分离数据库**
```sql
-- 用户域数据库
user_db:
- users
- user_profiles
- user_sessions
- enterprise_auth
-- 金融域数据库
financial_db:
- wallets
- transactions
- payment_orders
- billing_records
-- 产品域数据库
product_db:
- products
- product_subscriptions
- access_policies
- whitelists
-- 平台域数据库
platform_db:
- api_logs
- system_configs
- admin_users
- audit_logs
```
### 2. 数据一致性策略
**事件驱动架构**
```go
// 事件定义
type UserRegistered struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
Email string `json:"email"`
Timestamp time.Time `json:"timestamp"`
}
type WalletCreated struct {
UserID int64 `json:"user_id"`
WalletID string `json:"wallet_id"`
Balance float64 `json:"balance"`
Timestamp time.Time `json:"timestamp"`
}
// 事件处理
func (h *WalletEventHandler) HandleUserRegistered(event UserRegistered) error {
// 为新用户创建钱包
return h.walletService.CreateWallet(event.UserID)
}
```
## 🔧 开发流程重构
### 1. 代码生成和模板
**统一的代码生成器**
```bash
# 使用 protobuf 和 API 定义生成代码
make generate-service name=user-auth
make generate-api name=wallet
# 生成的目录结构
generated/
├── user-auth-service/
│ ├── pb/ # protobuf 生成
│ ├── client/ # 客户端代码
│ ├── server/ # 服务端代码
│ └── mock/ # 测试桩
```
### 2. 测试策略
**多层次测试**
```go
// 单元测试
func TestWalletService_Transfer(t *testing.T) {
// 领域逻辑测试
}
// 集成测试
func TestWalletAPI_Transfer(t *testing.T) {
// API 集成测试
}
// 契约测试
func TestWalletService_Contract(t *testing.T) {
// 服务间契约测试
}
// 端到端测试
func TestTransferWorkflow(t *testing.T) {
// 完整业务流程测试
}
```
### 3. CI/CD 流水线
**GitOps 工作流**
```yaml
# .github/workflows/service.yml
name: Service CI/CD
on:
push:
paths: ["services/user-auth/**"]
pull_request:
paths: ["services/user-auth/**"]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: make test-user-auth
build:
needs: test
runs-on: ubuntu-latest
steps:
- name: Build image
run: make build-user-auth
- name: Push to registry
run: make push-user-auth
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to staging
run: make deploy-user-auth-staging
```
## 📱 API 设计重构
### 1. RESTful API 标准化
**统一的 API 设计规范**
```go
// 资源命名
GET /api/v1/users # 获取用户列表
GET /api/v1/users/{id} # 获取用户详情
POST /api/v1/users # 创建用户
PUT /api/v1/users/{id} # 更新用户
DELETE /api/v1/users/{id} # 删除用户
// 子资源
GET /api/v1/users/{id}/wallets # 获取用户钱包
POST /api/v1/users/{id}/wallets # 创建用户钱包
// 统一响应格式
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
Meta *Meta `json:"meta,omitempty"`
}
type Meta struct {
Page int `json:"page,omitempty"`
PerPage int `json:"per_page,omitempty"`
Total int `json:"total,omitempty"`
TotalPages int `json:"total_pages,omitempty"`
}
```
### 2. GraphQL 支持
**为复杂查询提供 GraphQL**
```graphql
type User {
id: ID!
username: String!
email: String!
wallet: Wallet
subscriptions: [ProductSubscription!]!
}
type Query {
user(id: ID!): User
users(filter: UserFilter, pagination: Pagination): UserConnection
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
```
## 🔒 安全重构
### 1. 认证授权统一化
**OAuth 2.0 + RBAC**
```go
// 角色定义
type Role struct {
ID int64 `json:"id"`
Name string `json:"name"`
Permissions []Permission `json:"permissions"`
}
type Permission struct {
ID int64 `json:"id"`
Resource string `json:"resource"` // users, wallets, products
Action string `json:"action"` // read, write, delete
Scope string `json:"scope"` // own, team, all
}
// 中间件
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := extractToken(c)
claims, err := validateToken(token)
if err != nil {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
user, err := getUserFromClaims(claims)
if err != nil {
c.AbortWithStatus(http.StatusForbidden)
return
}
c.Set("user", user)
c.Next()
}
}
```
### 2. 数据安全
**端到端加密 + 数据脱敏**
```go
// 字段级加密
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email" encrypt:"true"`
Phone string `json:"phone" encrypt:"true" mask:"***-****-####"`
}
// 自动加密解密
func (u *User) BeforeSave() error {
return encrypt.EncryptStruct(u)
}
func (u *User) AfterFind() error {
return encrypt.DecryptStruct(u)
}
```
## 📈 监控和可观测性
### 1. 分布式追踪
**OpenTelemetry 集成**
```go
func (s *UserService) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.User, error) {
span := trace.SpanFromContext(ctx)
span.SetAttributes(
attribute.String("user.username", req.Username),
attribute.String("operation", "create_user"),
)
// 业务逻辑
user, err := s.userRepo.Create(ctx, req)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}
// 发送事件
event := UserCreatedEvent{UserID: user.ID, Username: user.Username}
if err := s.eventBus.Publish(ctx, event); err != nil {
s.logger.Error("failed to publish event", zap.Error(err))
}
return user, nil
}
```
### 2. 业务指标监控
**自定义业务指标**
```go
var (
userRegistrations = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "user_registrations_total",
Help: "Total number of user registrations",
},
[]string{"source", "status"},
)
walletBalance = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "wallet_balance",
Help: "Current wallet balance",
},
[]string{"user_id", "currency"},
)
)
```
## 🚀 迁移策略
### 阶段 1: 基础设施准备 (1-2 周)
1. 搭建新的开发环境
2. 建立 CI/CD 流水线
3. 配置监控和日志系统
### 阶段 2: 用户域重构 (2-3 周)
1. 重构用户认证服务
2. 迁移用户数据
3. 更新客户端集成
### 阶段 3: 金融域重构 (2-3 周)
1. 重构钱包服务
2. 重构支付服务
3. 数据一致性验证
### 阶段 4: 数据服务域重构 (3-4 周)
1. 重构数据网关
2. 重新组织业务 API
3. 性能优化
### 阶段 5: 产品域重构 (1-2 周)
1. 重构产品管理
2. 重构访问控制
3. 清理遗留代码
### 阶段 6: 平台域完善 (1-2 周)
1. 完善管理后台
2. 优化监控系统
3. 性能调优
## 📋 重构检查清单
### 代码质量
- [ ] 代码覆盖率 > 80%
- [ ] 静态代码分析通过
- [ ] API 文档完整
- [ ] 性能基准测试
### 架构质量
- [ ] 服务间松耦合
- [ ] 明确的业务边界
- [ ] 数据一致性保证
- [ ] 容错和恢复机制
### 运维质量
- [ ] 自动化部署
- [ ] 监控告警完整
- [ ] 日志可追溯
- [ ] 备份恢复测试
### 安全质量
- [ ] 认证授权机制
- [ ] 数据加密传输
- [ ] 漏洞扫描通过
- [ ] 安全策略文档
## 🎯 预期收益
1. **开发效率提升 40%** - 通过标准化和自动化
2. **系统可靠性提升 60%** - 通过完善的测试和监控
3. **维护成本降低 50%** - 通过清晰的架构和文档
4. **新功能交付速度提升 3 倍** - 通过模块化设计
重构是一个渐进的过程,建议分阶段实施,确保每个阶段都有明确的交付物和验收标准。

399
项目架构文档.md Normal file
View File

@@ -0,0 +1,399 @@
# 天远 API 服务器 - 微服务架构文档
## 项目概述
天远 API 服务器是一个基于 `go-zero` 框架构建的微服务架构系统,主要提供数据加密传输、用户管理、产品管理、支付等核心业务功能。整个系统采用微服务架构模式,每个服务独立部署,通过 gRPC 和 HTTP 进行通信。
## 技术栈
- **微服务框架**: go-zero
- **编程语言**: Go
- **数据库**: MySQL
- **缓存**: Redis
- **消息队列**: Kafka (通过 kq)
- **服务注册发现**: Etcd
- **API 协议**: HTTP/REST + gRPC
- **加密**: AES, MD5
- **第三方服务**: 阿里云短信、七牛云存储、百度 AI、支付宝支付
## 整体架构图
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 客户端应用 │ │ 管理端应用 │ │ 第三方系统 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ API 网关层 │
│ (gateway-api) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 微服务集群 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ API │ │ User │ │ Sentinel │ │ Admin │ │
│ │ Service │ │ Service │ │ Service │ │ Service │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ MQS │ │ Index │ │ WestDex │ │ 共享包 │ │
│ │ Service │ │ Service │ │ Module │ │ (pkg) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ MySQL │ │ Redis │ │ Kafka │ │ Etcd │ │
│ │ Database │ │ Cache │ │MessageQueue │ │Service Disc │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## 服务详细说明
### 1. Gateway Service (网关服务)
**路径**: `apps/gateway/`
**功能**:
- 统一入口网关,处理所有外部请求
- 用户认证与授权
- 企业认证管理
- 产品信息查询
- 用户产品管理
- 充值管理
- 白名单管理
**API 端点**:
- `/api/console/auth/*` - 认证相关(登录、注册、验证码)
- `/api/console/user/*` - 用户管理(用户信息、企业认证)
- `/api/console/product/*` - 产品管理
- `/api/console/user-product/*` - 用户产品关联
- `/api/console/topup/*` - 充值管理
- `/api/console/whitelist/*` - 白名单管理
**依赖**:
- UserRpc: 用户服务的 gRPC 客户端
- SentinelRpc: 哨兵服务的 gRPC 客户端
### 2. API Service (核心 API 服务)
**路径**: `apps/api/`
**功能**:
- 核心业务 API 处理
- 数据加密解密
- 第三方接口代理
- 业务逻辑处理
**业务模块**:
- **IVYZ**: 数据查询服务 (7 个 API 端点)
- **FLXG**: 风险评估服务 (12 个 API 端点)
- **QYGL**: 企业管理服务 (6 个 API 端点)
- **YYSY**: 应用系统服务 (7 个 API 端点)
- **JRZQ**: 金融征信服务 (4 个 API 端点)
- **COMB**: 组合业务服务 (1 个 API 端点)
**特点**:
- 所有 API 都需要通过 `ApiAuthInterceptor` 中间件认证
- 数据采用 AES 加密传输
- 集成了消息队列服务记录 API 调用
### 3. User Service (用户服务)
**路径**: `apps/user/`
**功能**:
- 用户注册、登录、认证
- 企业认证管理
- 钱包服务(余额、充值、扣款)
- API 请求记录管理
**gRPC 服务**:
- `Auth`: 用户认证服务
- `User`: 用户信息服务
- `WalletService`: 钱包服务
- `Enterprise`: 企业服务
- `ApiRequestService`: API 请求记录服务
**数据模型**:
- 用户信息
- 企业认证信息
- 钱包余额
- 扣款记录
- 充值记录
- API 请求记录
### 4. Sentinel Service (哨兵服务)
**路径**: `apps/sentinel/`
**功能**:
- 产品管理
- 用户产品关联管理
- 白名单管理
- 密钥管理
- 支付管理
**gRPC 服务**:
- `product`: 产品服务
- `userProduct`: 用户产品服务
- `whitelist`: 白名单服务
- `secret`: 密钥服务
- `topUp`: 充值服务
### 5. Admin Service (管理端服务)
**路径**: `apps/admin/`
**功能**:
- 管理员认证
- 企业审核管理
- 产品管理
- 用户管理
- 用户充值
**API 模块**:
- `auth`: 管理员登录
- `review`: 企业审核
- `product`: 产品 CRUD 操作
- `user`: 用户管理和充值
### 6. MQS Service (消息队列服务)
**路径**: `apps/mqs/`
**功能**:
- 消息队列消费者服务
- API 请求记录处理
- 异步任务处理
**特点**:
- 监听 Kafka 消息队列
- 处理 API 请求日志
- 支持分布式消息处理
### 7. Index Service (索引服务)
**路径**: `apps/index/`
**功能**:
- 产品索引服务
- 搜索功能支持
### 8. WestDex Module (西部对接模块)
**路径**: `westDex/`
**功能**:
- 第三方西部数据源对接
- 数据加密解密
- 独立运行的数据处理模块
## 共享包 (pkg/)
### 1. crypto
- AES 加密解密
- MD5 哈希
- 西部数据源专用加密
### 2. errs
- 统一错误处理
- API 错误码定义
### 3. jwt
- JWT 令牌生成和验证
### 4. models
- 消息队列数据模型
- 通用数据结构
### 5. response
- 统一响应格式
### 6. generate
- 支付相关生成器
### 7. sqlutil
- 数据库工具函数
## 中间件系统
### 1. AuthInterceptor
- 用户身份认证
- JWT 令牌验证
- 用户上下文注入
### 2. ApiAuthInterceptor
- API 调用认证
- 签名验证
- 限流控制
### 3. EntAuthInterceptor
- 企业认证检查
- 企业用户权限验证
### 4. ApiMqsInterceptor
- API 调用记录
- 消息队列发送
## 数据库设计
### 主要数据表
- `users`: 用户基础信息
- `enterprises`: 企业认证信息
- `products`: 产品信息
- `user_products`: 用户产品关联
- `wallets`: 钱包信息
- `recharge_records`: 充值记录
- `deduction_records`: 扣款记录
- `api_requests`: API 请求记录
- `pay_orders`: 支付订单
- `whitelists`: 白名单
- `secrets`: 密钥管理
## 配置管理
每个服务都支持多环境配置:
- `etc/*.yaml`: 生产环境配置
- `etc/*.dev.yaml`: 开发环境配置
**主要配置项**:
- 数据库连接 (MySQL)
- 缓存配置 (Redis)
- 服务发现 (Etcd)
- 第三方服务 (阿里云、七牛云、百度 AI)
- JWT 配置
- 消息队列配置
## 部署架构
### Docker 化部署
每个服务都有独立的 Dockerfile
- `apps/*/Dockerfile`
### 服务发现
- 使用 Etcd 作为服务注册中心
- gRPC 服务自动注册和发现
### 负载均衡
- 通过 go-zero 内置的负载均衡机制
- 支持多种负载均衡策略
## 安全机制
### 1. 数据传输安全
- AES 加密传输
- HTTPS 协议
- 签名验证
### 2. 身份认证
- JWT 令牌认证
- 多级权限控制
- 白名单机制
### 3. 数据存储安全
- 数据库密码加密
- 敏感信息脱敏
- 访问日志记录
## 监控与日志
### 日志系统
- 结构化日志记录
- 分级日志管理
- 链路追踪支持
### 监控指标
- API 调用统计
- 服务健康检查
- 性能指标监控
## 扩展性设计
### 水平扩展
- 无状态服务设计
- 数据库读写分离支持
- 缓存集群支持
### 模块化设计
- 微服务独立部署
- 插件化业务模块
- 模板化代码生成
## 开发规范
### 代码结构
每个微服务遵循 go-zero 标准结构:
```
service/
├── internal/
│ ├── config/ # 配置定义
│ ├── handler/ # HTTP处理器
│ ├── logic/ # 业务逻辑
│ ├── svc/ # 服务上下文
│ ├── types/ # 类型定义
│ └── middleware/ # 中间件
├── etc/ # 配置文件
├── *.api # API定义文件
├── *.proto # protobuf定义
└── Dockerfile # 容器化配置
```
### API 设计规范
- RESTful API 设计
- 统一错误码
- 标准化响应格式
- 版本控制支持
## 总结
天远 API 服务器采用了现代化的微服务架构,具备良好的可扩展性、可维护性和安全性。通过 go-zero 框架的支持,实现了高性能的服务间通信和统一的开发规范。整个系统支持多环境部署、监控告警、日志追踪等企业级特性,是一个完整的商业级 API 服务平台。