# MPC 分布式签名系统 - 架构设计文档 ## 1. 系统概述 本系统是一个基于多方安全计算 (MPC) 的分布式门限签名系统,支持 t-of-n 阈值签名方案。系统采用 **DDD (领域驱动设计) + 六边形架构 (Hexagonal Architecture) + 微服务** 的架构模式,使用 Go 语言开发,基于 bnb-chain/tss-lib 实现 TSS (Threshold Signature Scheme) 协议。 ### 1.1 核心特性 - **门限签名**: 支持任意 t-of-n 阈值方案 (如 2-of-3, 3-of-5, 4-of-7) - **分布式密钥生成 (DKG)**: 无需可信第三方生成密钥 - **ECDSA secp256k1**: 与以太坊/比特币兼容的签名算法 - **高安全性**: 密钥分片加密存储,单点泄露不影响安全性 - **微服务架构**: 可独立扩展和部署 - **Clean Architecture**: DDD + 六边形架构,领域逻辑与基础设施解耦 ### 1.2 技术栈 | 层级 | 技术选型 | |------|---------| | 语言 | Go 1.21+ | | TSS 库 | bnb-chain/tss-lib/v2 | | 通信协议 | gRPC + HTTP/REST | | 数据库 | PostgreSQL | | 缓存 | Redis | | 消息队列 | RabbitMQ | | 服务发现 | Consul | | 容器化 | Docker + Docker Compose | | 架构模式 | DDD + Hexagonal + Microservices | ### 1.3 架构设计原则 | 原则 | 说明 | |------|------| | **依赖倒置** | 内层定义接口,外层实现;依赖指向内层 | | **领域隔离** | Domain 层零外部依赖,纯业务逻辑 | | **端口适配** | 通过 Port/Adapter 模式解耦 I/O | | **服务自治** | 每个微服务独立部署、独立数据库 | | **单一职责** | 每个服务只负责一个业务领域 | ## 2. 软件架构模式 ### 2.1 DDD + 六边形架构 + 微服务 本系统采用三层架构模式的组合: ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ 微服务架构 (Microservices) │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Account Service │ │Session Coordinator│ │ Message Router │ ... │ │ │ (独立部署) │ │ (独立部署) │ │ (独立部署) │ │ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 六边形架构 (Hexagonal / Ports & Adapters) │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ Adapters (Input) │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ │ │HTTP Handler │ │gRPC Handler │ │ 消息消费者 │ │ │ │ │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ └─────────┼────────────────┼────────────────┼──────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ Application Layer │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ │ │ Input Ports │ │ Use Cases │ │ Output Ports │ │ │ │ │ │ │ │ (接口定义) │ │ (业务编排) │ │ (接口定义) │ │ │ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ Domain Layer (DDD) │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ │ │ Entities │ │Value Objects │ │Domain Services│ │ │ │ │ │ │ │ (实体) │ │ (值对象) │ │ (领域服务) │ │ │ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ │ │ Repositories │ │ Aggregates │ │ │ │ │ │ │ │ (仓储接口) │ │ (聚合根) │ │ │ │ │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ Adapters (Output) │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ │ │ PostgreSQL │ │ Redis │ │ RabbitMQ │ │ │ │ │ │ │ │ Repo │ │ Cache │ │ Publisher │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` ### 2.2 单个微服务内部结构 每个微服务采用六边形架构,内部分为三层: ``` service/ ├── domain/ # 领域层 (最内层,零外部依赖) │ ├── entities/ # 实体 - 有唯一标识的领域对象 │ ├── value_objects/ # 值对象 - 无标识的不可变对象 │ ├── repositories/ # 仓储接口 - 领域定义,适配器实现 │ └── services/ # 领域服务 - 跨实体的业务逻辑 │ ├── application/ # 应用层 (中间层,编排业务用例) │ ├── ports/ # 端口定义 │ │ ├── input/ # 入站端口 - 定义用例接口 │ │ └── output/ # 出站端口 - 定义基础设施接口 │ └── use_cases/ # 用例实现 - 业务流程编排 │ ├── adapters/ # 适配器层 (最外层,处理 I/O) │ ├── input/ # 入站适配器 │ │ ├── http/ # HTTP/REST 处理器 │ │ └── grpc/ # gRPC 处理器 │ └── output/ # 出站适配器 │ ├── postgres/ # PostgreSQL 仓储实现 │ ├── redis/ # Redis 缓存实现 │ └── rabbitmq/ # RabbitMQ 消息发布 │ └── cmd/server/ # 服务入口 - 依赖注入和启动 └── main.go ``` ### 2.3 DDD 战术模式实现 #### 2.3.1 实体 (Entity) 有唯一标识的领域对象,封装业务规则: ```go // services/account/domain/entities/account.go type Account struct { ID value_objects.AccountID // 唯一标识 Username string Email string PublicKey []byte Status value_objects.AccountStatus ThresholdN int ThresholdT int CreatedAt time.Time UpdatedAt time.Time } // 业务方法封装在实体内 func (a *Account) Suspend() error { if a.Status == value_objects.AccountStatusRecovering { return ErrAccountInRecovery } a.Status = value_objects.AccountStatusSuspended a.UpdatedAt = time.Now().UTC() return nil } func (a *Account) CanLogin() bool { return a.Status.CanLogin() } ``` #### 2.3.2 值对象 (Value Object) 无标识的不可变对象,通过值相等性比较: ```go // services/account/domain/value_objects/account_id.go type AccountID struct { value uuid.UUID } func NewAccountID() AccountID { return AccountID{value: uuid.New()} } func (id AccountID) String() string { return id.value.String() } func (id AccountID) Equals(other AccountID) bool { return id.value == other.value } // services/session-coordinator/domain/value_objects/session_status.go type SessionStatus string const ( SessionStatusCreated SessionStatus = "created" SessionStatusWaiting SessionStatus = "waiting" SessionStatusInProgress SessionStatus = "in_progress" SessionStatusCompleted SessionStatus = "completed" SessionStatusFailed SessionStatus = "failed" ) func (s SessionStatus) CanTransitionTo(target SessionStatus) bool { // 状态转换规则 transitions := map[SessionStatus][]SessionStatus{ SessionStatusCreated: {SessionStatusWaiting, SessionStatusFailed}, SessionStatusWaiting: {SessionStatusInProgress, SessionStatusFailed}, SessionStatusInProgress: {SessionStatusCompleted, SessionStatusFailed}, } for _, allowed := range transitions[s] { if allowed == target { return true } } return false } ``` #### 2.3.3 聚合 (Aggregate) 聚合根管理一组相关实体的一致性边界: ```go // services/session-coordinator/domain/entities/mpc_session.go // MPCSession 是聚合根,管理 Participants type MPCSession struct { ID uuid.UUID Type SessionType Status SessionStatus ThresholdT int ThresholdN int Participants []Participant // 子实体 CreatedAt time.Time } // 聚合根负责维护内部一致性 func (s *MPCSession) AddParticipant(p Participant) error { if len(s.Participants) >= s.ThresholdN { return ErrSessionFull } if s.Status != SessionStatusWaiting { return ErrInvalidSessionStatus } s.Participants = append(s.Participants, p) return nil } func (s *MPCSession) AllParticipantsReady() bool { if len(s.Participants) < s.ThresholdN { return false } for _, p := range s.Participants { if p.Status != ParticipantStatusReady { return false } } return true } ``` #### 2.3.4 仓储接口 (Repository) 领域层定义接口,适配器层实现: ```go // services/account/domain/repositories/account_repository.go type AccountRepository interface { Save(ctx context.Context, account *entities.Account) error FindByID(ctx context.Context, id value_objects.AccountID) (*entities.Account, error) FindByUsername(ctx context.Context, username string) (*entities.Account, error) FindByEmail(ctx context.Context, email string) (*entities.Account, error) Update(ctx context.Context, account *entities.Account) error Delete(ctx context.Context, id value_objects.AccountID) error } ``` #### 2.3.5 领域服务 (Domain Service) 跨实体的业务逻辑: ```go // services/session-coordinator/domain/services/session_coordinator.go type SessionCoordinatorService struct { sessionRepo repositories.SessionRepository } func (s *SessionCoordinatorService) ValidateThreshold(t, n int) error { if t < 1 || t > n { return ErrInvalidThreshold } if n < 2 { return ErrInvalidPartyCount } return nil } ``` ### 2.4 六边形架构端口与适配器 #### 2.4.1 入站端口 (Input Port) 定义用例接口: ```go // services/account/application/ports/input_ports.go type CreateAccountInput struct { Username string Email string PublicKey []byte ThresholdN int ThresholdT int } type CreateAccountOutput struct { Account *entities.Account } // 用例接口 type CreateAccountPort interface { Execute(ctx context.Context, input CreateAccountInput) (*CreateAccountOutput, error) } ``` #### 2.4.2 出站端口 (Output Port) 定义基础设施接口: ```go // services/session-coordinator/application/ports/output/session_storage_port.go type SessionStoragePort interface { Save(ctx context.Context, session *entities.MPCSession) error FindByID(ctx context.Context, id uuid.UUID) (*entities.MPCSession, error) Update(ctx context.Context, session *entities.MPCSession) error } // services/session-coordinator/application/ports/output/message_broker_port.go type MessageBrokerPort interface { PublishSessionEvent(ctx context.Context, event SessionEvent) error PublishPartyNotification(ctx context.Context, partyID string, msg []byte) error } ``` #### 2.4.3 用例实现 (Use Case) 编排业务流程: ```go // services/account/application/use_cases/create_account.go type CreateAccountUseCase struct { accountRepo repositories.AccountRepository eventPub ports.EventPublisherPort } func (uc *CreateAccountUseCase) Execute( ctx context.Context, input ports.CreateAccountInput, ) (*ports.CreateAccountOutput, error) { // 1. 创建领域实体 account := entities.NewAccount( input.Username, input.Email, input.PublicKey, input.ThresholdN, input.ThresholdT, ) // 2. 验证业务规则 if err := account.Validate(); err != nil { return nil, err } // 3. 持久化 if err := uc.accountRepo.Save(ctx, account); err != nil { return nil, err } // 4. 发布领域事件 uc.eventPub.Publish(ctx, events.AccountCreated{AccountID: account.ID}) return &ports.CreateAccountOutput{Account: account}, nil } ``` #### 2.4.4 入站适配器 (Input Adapter) HTTP/gRPC 处理器: ```go // services/account/adapters/input/http/account_handler.go type AccountHandler struct { createAccountUC ports.CreateAccountPort loginUC ports.LoginPort } func (h *AccountHandler) CreateAccount(c *gin.Context) { var req CreateAccountRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } output, err := h.createAccountUC.Execute(c.Request.Context(), ports.CreateAccountInput{ Username: req.Username, Email: req.Email, PublicKey: req.PublicKey, ThresholdN: req.ThresholdN, ThresholdT: req.ThresholdT, }) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(201, output) } ``` #### 2.4.5 出站适配器 (Output Adapter) 数据库/缓存实现: ```go // services/account/adapters/output/postgres/account_repo.go type PostgresAccountRepository struct { db *sql.DB } func (r *PostgresAccountRepository) Save(ctx context.Context, account *entities.Account) error { query := `INSERT INTO accounts (id, username, email, public_key, status, threshold_n, threshold_t, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)` _, err := r.db.ExecContext(ctx, query, account.ID.String(), account.Username, account.Email, account.PublicKey, account.Status, account.ThresholdN, account.ThresholdT, account.CreatedAt, ) return err } func (r *PostgresAccountRepository) FindByID(ctx context.Context, id value_objects.AccountID) (*entities.Account, error) { query := `SELECT id, username, email, public_key, status, threshold_n, threshold_t, created_at, updated_at FROM accounts WHERE id = $1` row := r.db.QueryRowContext(ctx, query, id.String()) // ... scan and return } ``` ### 2.5 依赖注入 (Dependency Injection) 在 main.go 中组装依赖: ```go // services/account/cmd/server/main.go func main() { // 基础设施 db := connectDatabase() redisClient := connectRedis() rabbitConn := connectRabbitMQ() // 出站适配器 (实现端口) accountRepo := postgres.NewAccountRepository(db) cacheAdapter := redis.NewCacheAdapter(redisClient) eventPublisher := rabbitmq.NewEventPublisher(rabbitConn) // 用例 (注入依赖) createAccountUC := use_cases.NewCreateAccountUseCase(accountRepo, eventPublisher) loginUC := use_cases.NewLoginUseCase(accountRepo, cacheAdapter) // 入站适配器 (使用用例) handler := http.NewAccountHandler(createAccountUC, loginUC) // 启动服务器 router := gin.Default() handler.RegisterRoutes(router) router.Run(":8080") } ``` ### 2.6 DDD 战术模式总览 | DDD 概念 | 本系统实现 | 位置 | |---------|-----------|------| | **Entity** | Account, MPCSession, Participant | `domain/entities/` | | **Value Object** | AccountID, SessionStatus, Threshold | `domain/value_objects/` | | **Aggregate** | Account (含 AccountShare), MPCSession (含 Participant) | `domain/entities/` | | **Repository** | AccountRepository, SessionRepository (接口) | `domain/repositories/` | | **Domain Service** | SessionCoordinatorService, AccountService | `domain/services/` | | **Application Service** | CreateAccountUseCase, JoinSessionUseCase | `application/use_cases/` | | **Input Port** | CreateAccountPort, LoginPort | `application/ports/input/` | | **Output Port** | SessionStoragePort, MessageBrokerPort | `application/ports/output/` | | **Input Adapter** | AccountHandler (HTTP), SessionGrpcHandler | `adapters/input/` | | **Output Adapter** | PostgresRepo, RedisCache, RabbitMQPublisher | `adapters/output/` | ## 3. 系统部署架构 ### 3.1 整体架构图 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ Client Layer │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ iOS App │ │ Android App │ │ Web Client │ │ │ │ (MPC SDK) │ │ (MPC SDK) │ │ │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ └─────────┼─────────────────┼─────────────────┼───────────────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ API Gateway (HTTP/gRPC) │ └─────────────────────────────┬───────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Account │ │ Session │ │ Message │ │ Service │ │ Coordinator │ │ Router │ │ (用户/账户管理) │ │ (会话协调) │ │ (消息路由) │ │ │ │ │ │ │ │ Port: 50054 │ │ Port: 50051 │ │ Port: 50052 │ │ HTTP: 8083 │ │ HTTP: 8080 │ │ HTTP: 8081 │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Server Party │ │ │ │ Service x N │◄──────────┘ │ │ (MPC 计算节点) │ │ │ │ │ │ Party 1: 50053 │ │ │ Party 2: 50055 │ │ │ Party 3: 50056 │ │ └────────┬────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ Data Layer │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ PostgreSQL │ │ Redis │ │ RabbitMQ │ │ │ │ (持久化) │ │ (缓存/会话) │ │ (消息队列) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` ### 3.2 服务职责 #### 3.2.1 Session Coordinator (会话协调器) - 创建和管理 MPC 会话 - 协调参与方加入会话 - 跟踪会话状态和进度 - 管理参与方就绪状态 #### 3.2.2 Message Router (消息路由器) - 路由 TSS 协议消息 - 支持点对点和广播消息 - 消息缓存和重传 - WebSocket 实时通信 #### 3.2.3 Server Party (服务端参与方) - 作为 MPC 协议的服务端参与方 - 执行 DKG 和签名协议 - 安全存储加密的密钥分片 - 支持多实例部署 #### 3.2.4 Account Service (账户服务) - 用户注册和认证 - 账户管理 - MPC 会话入口 API - 账户恢复流程 ## 4. 核心流程 ### 4.1 密钥生成流程 (Keygen) ``` ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Client │ │Coordinator│ │ Router │ │ Parties │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ CreateSession │ │ │ │──────────────>│ │ │ │ │ │ │ │ SessionID + │ │ │ │ JoinTokens │ │ │ │<──────────────│ │ │ │ │ │ │ │ JoinSession (各参与方) │ │──────────────────────────────────────────────>│ │ │ │ │ │ MarkReady (各参与方) │ │──────────────────────────────────────────────>│ │ │ │ │ │ StartSession │ │ │──────────────>│ │ │ │ │ Notify Start │ │ │ │──────────────────────────────>│ │ │ │ │ │ │ TSS Messages (多轮) │ │ │ │<─────────────>│ │ │ │ │ │ │ ReportCompletion │ │ │<──────────────────────────────│ │ │ │ │ │ Session Completed │ │ │<──────────────│ │ │ │ │ │ │ ``` ### 4.2 签名流程 (Signing) ``` ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Client │ │Coordinator│ │ Router │ │ Parties │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ CreateSignSession │ │ │ (messageHash) │ │ │ │──────────────>│ │ │ │ │ │ │ │ SessionID │ │ │ │<──────────────│ │ │ │ │ │ │ │ JoinSession (t+1 参与方) │ │ │──────────────────────────────────────────────>│ │ │ │ │ │ StartSession │ │ │──────────────>│ │ │ │ │ │ │ │ │ TSS Messages (多轮) │ │ │ │<─────────────>│ │ │ │ │ │ │ Signature │ │ │ │<──────────────────────────────│ │ │ │ │ │ Signature (R, S, V) │ │ │<──────────────│ │ │ │ │ │ │ ``` ## 5. 数据模型 ### 5.1 Session (会话) ```go type Session struct { ID uuid.UUID // 会话唯一标识 Type SessionType // keygen | sign Status SessionStatus // created | waiting | in_progress | completed | failed ThresholdT int // 签名阈值 (t+1 签名者) ThresholdN int // 总参与方数 MessageHash []byte // 待签名消息哈希 (签名会话) Participants []Participant // 参与方列表 CreatedAt time.Time ExpiresAt time.Time } ``` ### 5.2 Participant (参与方) ```go type Participant struct { PartyID string // 参与方标识 PartyIndex int // 协议中的索引 DeviceInfo DeviceInfo // 设备信息 Status ParticipantStatus // joined | ready | computing | completed JoinToken string // 加入令牌 } ``` ### 5.3 KeyShare (密钥分片) ```go type KeyShare struct { ID uuid.UUID AccountID uuid.UUID PartyID string EncryptedShareData []byte // AES-GCM 加密的分片数据 PublicKey []byte // 组公钥 CreatedAt time.Time } ``` ## 6. 安全设计 ### 6.1 密钥安全 - **密钥分片存储**: 使用 AES-256-GCM 加密存储 - **主密钥管理**: 从环境变量或 KMS 加载 - **无单点故障**: 任意 t 个节点被攻破不影响安全性 ### 6.2 通信安全 - **TLS 加密**: 所有 gRPC/HTTP 通信使用 TLS - **消息认证**: TSS 消息包含参与方签名 - **会话令牌**: 使用 UUID v4 生成一次性令牌 ### 6.3 安全属性 | 属性 | 描述 | |------|------| | 门限安全 | 需要至少 t+1 方参与才能签名 | | 密钥不可恢复 | 少于 t+1 个分片无法恢复私钥 | | 前向安全 | 会话密钥独立,历史泄露不影响未来 | | 抗合谋 | t 个恶意方无法伪造签名 | ## 7. 部署架构 ### 7.1 最小部署 (2-of-3) ``` ┌─────────────────────────────────────────────────────────┐ │ Server 1 (Coordinator) │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Session Coord. │ │ Message Router │ │ │ │ Port: 50051 │ │ Port: 50052 │ │ │ └─────────────────┘ └─────────────────┘ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Account Service │ │ PostgreSQL │ │ │ │ Port: 50054 │ │ Redis/RabbitMQ │ │ │ └─────────────────┘ └─────────────────┘ │ └─────────────────────────────────────────────────────────┘ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ Server Party 1 │ │ Server Party 2 │ │ Server Party 3 │ │ Port: 50053 │ │ Port: 50055 │ │ Port: 50056 │ └──────────────────┘ └──────────────────┘ └──────────────────┘ ``` ### 7.2 生产环境部署 - **高可用**: 每个服务至少 2 副本 - **负载均衡**: Nginx/Traefik 反向代理 - **服务发现**: Consul 集群 - **监控**: Prometheus + Grafana ## 8. 目录结构 ``` mpc-system/ ├── api/ # API 定义 │ ├── grpc/ # gRPC 生成代码 │ └── proto/ # Protobuf 定义 ├── docs/ # 文档 ├── migrations/ # 数据库迁移 ├── pkg/ # 公共包 │ ├── crypto/ # 加密工具 │ └── tss/ # TSS 封装 ├── services/ # 微服务 │ ├── account/ # 账户服务 │ ├── message-router/ # 消息路由 │ ├── server-party/ # 服务端参与方 │ └── session-coordinator/ # 会话协调 ├── tests/ # 测试 │ ├── e2e/ # 端到端测试 │ ├── integration/ # 集成测试 │ └── unit/ # 单元测试 ├── docker-compose.yml # Docker 编排 ├── Makefile # 构建脚本 └── go.mod # Go 模块 ```