745 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			745 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # 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 框架,保持了你原有业务逻辑的同时,提供了清晰的服务边界和强大的扩展能力。每个服务都可以独立开发、测试、部署和扩容。
 |