Golang项目实战:从0到1开发RESTful后端(分层架构、DDD、单元测试、Docker化)

1. 项目架构设计

1.1 分层架构概述

API Gateway
Presentation Layer
Application Layer
Domain Layer
Infrastructure Layer
HTTP Handlers
Use Cases/Services
Domain Models
Database/External Services

1.2 项目结构

ecommerce-api/
├── cmd/
│   ├── api/
│   │   └── main.go
│   └── migrate/
│       └── main.go
├── internal/
│   ├── domain/
│   │   ├── entity/
│   │   ├── valueobject/
│   │   ├── repository/
│   │   └── service/
│   ├── application/
│   │   ├── dto/
│   │   ├── usecase/
│   │   └── mapper/
│   ├── infrastructure/
│   │   ├── persistence/
│   │   ├── http/
│   │   ├── config/
│   │   └── logger/
│   └── interface/
│       ├── http/
│       ├── grpc/
│       └── websocket/
├── pkg/
│   ├── errors/
│   ├── validator/
│   ├── jwt/
│   └── response/
├── migrations/
├── scripts/
├── tests/
├── docker/
├── config/
├── Makefile
├── go.mod
├── go.sum
├── Dockerfile
├── docker-compose.yml
└── README.md

2. 领域驱动设计 (DDD) 实现

2.1 领域模型定义

// internal/domain/entity/user.go
package entity

import (
    "time"
    "errors"
    "regexp"
    
    "github.com/google/uuid"
)

// User 聚合根
type User struct {
    id        uuid.UUID
    username  string
    email     string
    password  string
    profile   *UserProfile
    status    UserStatus
    createdAt time.Time
    updatedAt time.Time
    orders    []Order // 关联订单
}

// UserProfile 值对象
type UserProfile struct {
    firstName string
    lastName  string
    avatar    string
    phone     string
    address   *Address
}

// Address 值对象
type Address struct {
    street     string
    city       string
    state      string
    country    string
    postalCode string
}

// UserStatus 枚举
type UserStatus string

const (
    UserStatusActive   UserStatus = "active"
    UserStatusInactive UserStatus = "inactive"
    UserStatusBanned   UserStatus = "banned"
)

// 构造函数
func NewUser(username, email, password string) (*User, error) {
    if err := validateUsername(username); err != nil {
        return nil, err
    }
    
    if err := validateEmail(email); err != nil {
        return nil, err
    }
    
    if err := validatePassword(password); err != nil {
        return nil, err
    }
    
    hashedPassword, err := hashPassword(password)
    if err != nil {
        return nil, err
    }
    
    now := time.Now()
    
    return &User{
        id:        uuid.New(),
        username:  username,
        email:     email,
        password:  hashedPassword,
        profile:   nil,
        status:    UserStatusActive,
        createdAt: now,
        updatedAt: now,
        orders:    make([]Order, 0),
    }, nil
}

// 领域方法
func (u *User) UpdateProfile(profile *UserProfile) error {
    if profile == nil {
        return errors.New("profile cannot be nil")
    }
    
    u.profile = profile
    u.updatedAt = time.Now()
    
    return nil
}

func (u *User) ChangeEmail(newEmail string) error {
    if err := validateEmail(newEmail); err != nil {
        return err
    }
    
    if u.email == newEmail {
        return errors.New("email is the same")
    }
    
    u.email = newEmail
    u.updatedAt = time.Now()
    
    return nil
}

func (u *User) Activate() error {
    if u.status == UserStatusActive {
        return errors.New("user is already active")
    }
    
    u.status = UserStatusActive
    u.updatedAt = time.Now()
    
    return nil
}

func (u *User) Deactivate() error {
    if u.status == UserStatusInactive {
        return errors.New("user is already inactive")
    }
    
    u.status = UserStatusInactive
    u.updatedAt = time.Now()
    
    return nil
}

func (u *User) AddOrder(order Order) {
    u.orders = append(u.orders, order)
    u.updatedAt = time.Now()
}

// 获取器
func (u *User) ID() uuid.UUID        { return u.id }
func (u *User) Username() string     { return u.username }
func (u *User) Email() string        { return u.email }
func (u *User) Password() string     { return u.password }
func (u *User) Profile() *UserProfile { return u.profile }
func (u *User) Status() UserStatus   { return u.status }
func (u *User) CreatedAt() time.Time { return u.createdAt }
func (u *User) UpdatedAt() time.Time { return u.updatedAt }
func (u *User) Orders() []Order      { return u.orders }

// 验证函数
func validateUsername(username string) error {
    if len(username) < 3 || len(username) > 20 {
        return errors.New("username must be between 3 and 20 characters")
    }
    
    matched, _ := regexp.MatchString(`^[a-zA-Z0-9_]+$`, username)
    if !matched {
        return errors.New("username can only contain letters, numbers, and underscores")
    }
    
    return nil
}

func validateEmail(email string) error {
    emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
    if !emailRegex.MatchString(email) {
        return errors.New("invalid email format")
    }
    return nil
}

func validatePassword(password string) error {
    if len(password) < 8 {
        return errors.New("password must be at least 8 characters long")
    }
    
    hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password)
    hasLower := regexp.MustCompile(`[a-z]`).MatchString(password)
    hasDigit := regexp.MustCompile(`[0-9]`).MatchString(password)
    hasSpecial := regexp.MustCompile(`[!@#$%^&*(),.?":{}|<>]`).MatchString(password)
    
    if !hasUpper || !hasLower || !hasDigit || !hasSpecial {
        return errors.New("password must contain uppercase, lowercase, digit, and special character")
    }
    
    return nil
}

func hashPassword(password string) (string, error) {
    // 实际实现中会使用bcrypt等库
    return password, nil
}

2.2 领域服务

// internal/domain/service/user_service.go
package service

import (
    "context"
    "errors"
    
    "ecommerce-api/internal/domain/entity"
    "ecommerce-api/internal/domain/repository"
    "ecommerce-api/internal/domain/valueobject"
)

// UserService 领域服务
type UserService struct {
    userRepo repository.UserRepository
    eventBus EventBus
}

func NewUserService(userRepo repository.UserRepository, eventBus EventBus) *UserService {
    return &UserService{
        userRepo: userRepo,
        eventBus: eventBus,
    }
}

// RegisterUser 用户注册
func (s *UserService) RegisterUser(ctx context.Context, username, email, password string) (*entity.User, error) {
    // 检查用户名是否已存在
    existingUser, err := s.userRepo.FindByUsername(ctx, username)
    if err != nil {
        return nil, err
    }
    if existingUser != nil {
        return nil, errors.New("username already exists")
    }
    
    // 检查邮箱是否已存在
    existingUser, err = s.userRepo.FindByEmail(ctx, email)
    if err != nil {
        return nil, err
    }
    if existingUser != nil {
        return nil, errors.New("email already exists")
    }
    
    // 创建新用户
    user, err := entity.NewUser(username, email, password)
    if err != nil {
        return nil, err
    }
    
    // 保存用户
    if err := s.userRepo.Save(ctx, user); err != nil {
        return nil, err
    }
    
    // 发布用户注册事件
    event := valueobject.NewUserRegisteredEvent(user.ID(), user.Username(), user.Email())
    if err := s.eventBus.Publish(ctx, event); err != nil {
        // 记录错误但不影响主流程
        log.Printf("Failed to publish user registered event: %v", err)
    }
    
    return user, nil
}

// UpdateUserProfile 更新用户资料
func (s *UserService) UpdateUserProfile(ctx context.Context, userID string, profile *entity.UserProfile) error {
    user, err := s.userRepo.FindByID(ctx, userID)
    if err != nil {
        return err
    }
    if user == nil {
        return errors.New("user not found")
    }
    
    if err := user.UpdateProfile(profile); err != nil {
        return err
    }
    
    if err := s.userRepo.Update(ctx, user); err != nil {
        return err
    }
    
    // 发布用户资料更新事件
    event := valueobject.NewUserProfileUpdatedEvent(userID)
    if err := s.eventBus.Publish(ctx, event); err != nil {
        log.Printf("Failed to publish user profile updated event: %v", err)
    }
    
    return nil
}

// ActivateUser 激活用户
func (s *UserService) ActivateUser(ctx context.Context, userID string) error {
    user, err := s.userRepo.FindByID(ctx, userID)
    if err != nil {
        return err
    }
    if user == nil {
        return errors.New("user not found")
    }
    
    if err := user.Activate(); err != nil {
        return err
    }
    
    if err := s.userRepo.Update(ctx, user); err != nil {
        return err
    }
    
    return nil
}

// GetUserWithOrders 获取用户及其订单
func (s *UserService) GetUserWithOrders(ctx context.Context, userID string) (*entity.User, error) {
    user, err := s.userRepo.FindByIDWithOrders(ctx, userID)
    if err != nil {
        return nil, err
    }
    if user == nil {
        return nil, errors.New("user not found")
    }
    
    return user, nil
}

// ValidateCredentials 验证用户凭据
func (s *UserService) ValidateCredentials(ctx context.Context, username, password string) (*entity.User, error) {
    user, err := s.userRepo.FindByUsername(ctx, username)
    if err != nil {
        return nil, err
    }
    if user == nil {
        return nil, errors.New("invalid credentials")
    }
    
    // 验证密码
    if !verifyPassword(password, user.Password()) {
        return nil, errors.New("invalid credentials")
    }
    
    return user, nil
}

func verifyPassword(password, hash string) bool {
    // 实际实现中会使用bcrypt.CompareHashAndPassword
    return password == hash
}

2.3 仓储接口定义

// internal/domain/repository/user_repository.go
package repository

import (
    "context"
    
    "github.com/google/uuid"
    "ecommerce-api/internal/domain/entity"
)

// UserRepository 用户仓储接口
type UserRepository interface {
    // 基本CRUD操作
    Save(ctx context.Context, user *entity.User) error
    Update(ctx context.Context, user *entity.User) error
    Delete(ctx context.Context, userID string) error
    FindByID(ctx context.Context, userID string) (*entity.User, error)
    
    // 查询方法
    FindByUsername(ctx context.Context, username string) (*entity.User, error)
    FindByEmail(ctx context.Context, email string) (*entity.User, error)
    FindAll(ctx context.Context, limit, offset int) ([]*entity.User, error)
    FindByStatus(ctx context.Context, status entity.UserStatus, limit, offset int) ([]*entity.User, error)
    
    // 复杂查询
    FindByIDWithOrders(ctx context.Context, userID string) (*entity.User, error)
    FindByCreatedAtRange(ctx context.Context, start, end time.Time) ([]*entity.User, error)
    
    // 统计
    Count(ctx context.Context) (int64, error)
    CountByStatus(ctx context.Context, status entity.UserStatus) (int64, error)
    
    // 事务支持
    WithTx(ctx context.Context, fn func(ctx context.Context) error) error
}

// 查询规格模式
type UserSpecification interface {
    IsSatisfiedBy(user *entity.User) bool
    ToSQL() string
    Parameters() []interface{}
}

// 组合规格
type AndSpecification struct {
    specs []UserSpecification
}

func NewAndSpecification(specs ...UserSpecification) *AndSpecification {
    return &AndSpecification{specs: specs}
}

func (s *AndSpecification) IsSatisfiedBy(user *entity.User) bool {
    for _, spec := range s.specs {
        if !spec.IsSatisfiedBy(user) {
            return false
        }
    }
    return true
}

func (s *AndSpecification) ToSQL() string {
    // 实现SQL生成逻辑
    return ""
}

func (s *AndSpecification) Parameters() []interface{} {
    // 实现参数提取逻辑
    return nil
}

// 具体规格实现
type ActiveUserSpecification struct{}

func (s *ActiveUserSpecification) IsSatisfiedBy(user *entity.User) bool {
    return user.Status() == entity.UserStatusActive
}

func (s *ActiveUserSpecification) ToSQL() string {
    return "status = ?"
}

func (s *ActiveUserSpecification) Parameters() []interface{} {
    return []interface{}{entity.UserStatusActive}
}

type EmailDomainSpecification struct {
    domain string
}

func NewEmailDomainSpecification(domain string) *EmailDomainSpecification {
    return &EmailDomainSpecification{domain: domain}
}

func (s *EmailDomainSpecification) IsSatisfiedBy(user *entity.User) bool {
    // 简化的邮箱域名检查
    return len(user.Email()) > len(s.domain) && 
           user.Email()[len(user.Email())-len(s.domain):] == s.domain
}

func (s *EmailDomainSpecification) ToSQL() string {
    return "email LIKE ?"
}

func (s *EmailDomainSpecification) Parameters() []interface{} {
    return []interface{}{"%" + s.domain}
}

3. 应用层实现

3.1 DTO定义

// internal/application/dto/user_dto.go
package dto

import (
    "time"
    
    "github.com/google/uuid"
)

// CreateUserDTO 创建用户DTO
type CreateUserDTO struct {
    Username string `json:"username" validate:"required,min=3,max=20"`
    Email    string `json:"email" validate:"required,email"`
    Password string `json:"password" validate:"required,min=8"`
    FirstName string `json:"first_name" validate:"max=50"`
    LastName  string `json:"last_name" validate:"max=50"`
    Phone     string `json:"phone" validate:"omitempty,e164"`
}

// UpdateUserDTO 更新用户DTO
type UpdateUserDTO struct {
    UserID    string `json:"user_id" validate:"required,uuid"`
    Username  string `json:"username,omitempty" validate:"omitempty,min=3,max=20"`
    Email     string `json:"email,omitempty" validate:"omitempty,email"`
    FirstName string `json:"first_name,omitempty" validate:"omitempty,max=50"`
    LastName  string `json:"last_name,omitempty" validate:"omitempty,max=50"`
    Phone     string `json:"phone,omitempty" validate:"omitempty,e164"`
    Avatar    string `json:"avatar,omitempty" validate:"omitempty,url"`
}

// UserResponseDTO 用户响应DTO
type UserResponseDTO struct {
    UserID    string    `json:"user_id"`
    Username  string    `json:"username"`
    Email     string    `json:"email"`
    FirstName string    `json:"first_name"`
    LastName  string    `json:"last_name"`
    Avatar    string    `json:"avatar"`
    Phone     string    `json:"phone"`
    Status    string    `json:"status"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

// ListUsersDTO 用户列表DTO
type ListUsersDTO struct {
    Page     int    `json:"page" validate:"min=1"`
    PageSize int    `json:"page_size" validate:"min=1,max=100"`
    Status   string `json:"status,omitempty" validate:"omitempty,oneof=active inactive banned"`
    Search   string `json:"search,omitempty" validate:"omitempty,max=100"`
}

// ListUsersResponseDTO 用户列表响应DTO
type ListUsersResponseDTO struct {
    Total    int64              `json:"total"`
    Page     int                `json:"page"`
    PageSize int                `json:"page_size"`
    Users    []UserResponseDTO  `json:"users"`
}

// LoginDTO 登录DTO
type LoginDTO struct {
    Username string `json:"username" validate:"required"`
    Password string `json:"password" validate:"required"`
}

// LoginResponseDTO 登录响应DTO
type LoginResponseDTO struct {
    AccessToken  string    `json:"access_token"`
    RefreshToken string    `json:"refresh_token"`
    TokenType    string    `json:"token_type"`
    ExpiresAt    time.Time `json:"expires_at"`
    User         UserResponseDTO `json:"user"`
}

// ErrorDTO 错误响应DTO
type ErrorDTO struct {
    Code    string      `json:"code"`
    Message string      `json:"message"`
    Details interface{} `json:"details,omitempty"`
    Path    string      `json:"path,omitempty"`
    Method  string      `json:"method,omitempty"`
    Timestamp time.Time `json:"timestamp"`
}

// ValidationErrorDTO 验证错误DTO
type ValidationErrorDTO struct {
    Field   string `json:"field"`
    Message string `json:"message"`
    Value   interface{} `json:"value,omitempty"`
}

3.2 Use Case实现

// internal/application/usecase/user_usecase.go
package usecase

import (
    "context"
    "errors"
    
    "github.com/google/uuid"
    
    "ecommerce-api/internal/application/dto"
    "ecommerce-api/internal/application/mapper"
    "ecommerce-api/internal/domain/entity"
    "ecommerce-api/internal/domain/repository"
    "ecommerce-api/internal/domain/service"
    "ecommerce-api/internal/infrastructure/logger"
)

// UserUseCase 用户用例
type UserUseCase struct {
    userService service.UserService
    userRepo    repository.UserRepository
    logger      logger.Logger
}

func NewUserUseCase(userService service.UserService, userRepo repository.UserRepository, logger logger.Logger) *UserUseCase {
    return &UserUseCase{
        userService: userService,
        userRepo:    userRepo,
        logger:      logger,
    }
}

// CreateUser 创建用户
func (uc *UserUseCase) CreateUser(ctx context.Context, req dto.CreateUserDTO) (*dto.UserResponseDTO, error) {
    uc.logger.Info("Creating user", "username", req.Username, "email", req.Email)
    
    // 调用领域服务创建用户
    user, err := uc.userService.RegisterUser(ctx, req.Username, req.Email, req.Password)
    if err != nil {
        uc.logger.Error("Failed to create user", "error", err)
        return nil, err
    }
    
    // 如果有额外的profile信息,更新用户资料
    if req.FirstName != "" || req.LastName != "" || req.Phone != "" {
        profile := &entity.UserProfile{
            FirstName: req.FirstName,
            LastName:  req.LastName,
            Phone:     req.Phone,
        }
        
        if err := uc.userService.UpdateUserProfile(ctx, user.ID().String(), profile); err != nil {
            uc.logger.Error("Failed to update user profile", "error", err)
            // 不返回错误,因为用户已经创建成功
        }
    }
    
    // 转换为响应DTO
    response := mapper.UserToResponseDTO(user)
    
    uc.logger.Info("User created successfully", "user_id", user.ID().String())
    
    return response, nil
}

// GetUser 获取用户信息
func (uc *UserUseCase) GetUser(ctx context.Context, userID string) (*dto.UserResponseDTO, error) {
    uc.logger.Info("Getting user", "user_id", userID)
    
    user, err := uc.userRepo.FindByID(ctx, userID)
    if err != nil {
        uc.logger.Error("Failed to get user", "user_id", userID, "error", err)
        return nil, err
    }
    
    if user == nil {
        uc.logger.Warn("User not found", "user_id", userID)
        return nil, errors.New("user not found")
    }
    
    response := mapper.UserToResponseDTO(user)
    
    uc.logger.Info("User retrieved successfully", "user_id", userID)
    
    return response, nil
}

// UpdateUser 更新用户信息
func (uc *UserUseCase) UpdateUser(ctx context.Context, req dto.UpdateUserDTO) (*dto.UserResponseDTO, error) {
    uc.logger.Info("Updating user", "user_id", req.UserID)
    
    // 检查用户是否存在
    existingUser, err := uc.userRepo.FindByID(ctx, req.UserID)
    if err != nil {
        uc.logger.Error("Failed to find user", "user_id", req.UserID, "error", err)
        return nil, err
    }
    
    if existingUser == nil {
        uc.logger.Warn("User not found for update", "user_id", req.UserID)
        return nil, errors.New("user not found")
    }
    
    // 如果有需要更新的字段
    if req.Username != "" || req.Email != "" {
        if req.Username != "" {
            existingUser.UpdateUsername(req.Username)
        }
        if req.Email != "" {
            if err := existingUser.ChangeEmail(req.Email); err != nil {
                return nil, err
            }
        }
        
        if err := uc.userRepo.Update(ctx, existingUser); err != nil {
            uc.logger.Error("Failed to update user", "user_id", req.UserID, "error", err)
            return nil, err
        }
    }
    
    // 如果有profile信息需要更新
    if req.FirstName != "" || req.LastName != "" || req.Phone != "" || req.Avatar != "" {
        profile := &entity.UserProfile{
            FirstName: req.FirstName,
            LastName:  req.LastName,
            Phone:     req.Phone,
            Avatar:    req.Avatar,
        }
        
        if err := uc.userService.UpdateUserProfile(ctx, req.UserID, profile); err != nil {
            uc.logger.Error("Failed to update user profile", "user_id", req.UserID, "error", err)
            return nil, err
        }
    }
    
    // 重新获取更新后的用户
    updatedUser, err := uc.userRepo.FindByID(ctx, req.UserID)
    if err != nil {
        uc.logger.Error("Failed to get updated user", "user_id", req.UserID, "error", err)
        return nil, err
    }
    
    response := mapper.UserToResponseDTO(updatedUser)
    
    uc.logger.Info("User updated successfully", "user_id", req.UserID)
    
    return response, nil
}

// ListUsers 获取用户列表
func (uc *UserUseCase) ListUsers(ctx context.Context, req dto.ListUsersDTO) (*dto.ListUsersResponseDTO, error) {
    uc.logger.Info("Listing users", "page", req.Page, "page_size", req.PageSize, "status", req.Status)
    
    limit := req.PageSize
    offset := (req.Page - 1) * req.PageSize
    
    var users []*entity.User
    var total int64
    var err error
    
    // 根据状态筛选
    if req.Status != "" {
        status := entity.UserStatus(req.Status)
        users, err = uc.userRepo.FindByStatus(ctx, status, limit, offset)
        if err != nil {
            uc.logger.Error("Failed to find users by status", "status", req.Status, "error", err)
            return nil, err
        }
        total, err = uc.userRepo.CountByStatus(ctx, status)
    } else {
        users, err = uc.userRepo.FindAll(ctx, limit, offset)
        if err != nil {
            uc.logger.Error("Failed to find all users", "error", err)
            return nil, err
        }
        total, err = uc.userRepo.Count(ctx)
    }
    
    if err != nil {
        uc.logger.Error("Failed to count users", "error", err)
        return nil, err
    }
    
    // 转换为响应DTO
    userDTOs := make([]dto.UserResponseDTO, len(users))
    for i, user := range users {
        userDTOs[i] = *mapper.UserToResponseDTO(user)
    }
    
    response := &dto.ListUsersResponseDTO{
        Total:    total,
        Page:     req.Page,
        PageSize: req.PageSize,
        Users:    userDTOs,
    }
    
    uc.logger.Info("Users listed successfully", "count", len(users), "total", total)
    
    return response, nil
}

// DeleteUser 删除用户
func (uc *UserUseCase) DeleteUser(ctx context.Context, userID string) error {
    uc.logger.Info("Deleting user", "user_id", userID)
    
    user, err := uc.userRepo.FindByID(ctx, userID)
    if err != nil {
        uc.logger.Error("Failed to find user", "user_id", userID, "error", err)
        return err
    }
    
    if user == nil {
        uc.logger.Warn("User not found for deletion", "user_id", userID)
        return errors.New("user not found")
    }
    
    // 检查是否有未完成的订单
    if len(user.Orders()) > 0 {
        uc.logger.Warn("Cannot delete user with orders", "user_id", userID, "order_count", len(user.Orders()))
        return errors.New("cannot delete user with existing orders")
    }
    
    if err := uc.userRepo.Delete(ctx, userID); err != nil {
        uc.logger.Error("Failed to delete user", "user_id", userID, "error", err)
        return err
    }
    
    uc.logger.Info("User deleted successfully", "user_id", userID)
    
    return nil
}

// Login 用户登录
func (uc *UserUseCase) Login(ctx context.Context, req dto.LoginDTO) (*dto.LoginResponseDTO, error) {
    uc.logger.Info("User login attempt", "username", req.Username)
    
    // 验证用户凭据
    user, err := uc.userService.ValidateCredentials(ctx, req.Username, req.Password)
    if err != nil {
        uc.logger.Warn("Invalid login credentials", "username", req.Username, "error", err)
        return nil, errors.New("invalid credentials")
    }
    
    // 检查用户状态
    if user.Status() != entity.UserStatusActive {
        uc.logger.Warn("Login attempt with inactive user", "username", req.Username, "status", user.Status())
        return nil, errors.New("user account is not active")
    }
    
    // 生成token
    accessToken, refreshToken, expiresAt, err := uc.generateTokens(user.ID().String(), user.Username())
    if err != nil {
        uc.logger.Error("Failed to generate tokens", "username", req.Username, "error", err)
        return nil, err
    }
    
    response := &dto.LoginResponseDTO{
        AccessToken:  accessToken,
        RefreshToken: refreshToken,
        TokenType:    "Bearer",
        ExpiresAt:    expiresAt,
        User:         *mapper.UserToResponseDTO(user),
    }
    
    uc.logger.Info("User logged in successfully", "username", req.Username, "user_id", user.ID().String())
    
    return response, nil
}

func (uc *UserUseCase) generateTokens(userID, username string) (accessToken, refreshToken string, expiresAt time.Time, err error) {
    // 实际实现中会使用JWT库生成token
    accessToken = "access-token-" + userID
    refreshToken = "refresh-token-" + userID
    expiresAt = time.Now().Add(24 * time.Hour)
    
    return accessToken, refreshToken, expiresAt, nil
}

4. 基础设施层实现

4.1 数据库模型和映射

// internal/infrastructure/persistence/model/user_model.go
package model

import (
    "time"
    
    "gorm.io/gorm"
    "github.com/google/uuid"
)

// UserModel GORM模型
type UserModel struct {
    ID           uuid.UUID  `gorm:"type:uuid;primary_key"`
    Username     string     `gorm:"type:varchar(20);uniqueIndex;not null"`
    Email        string     `gorm:"type:varchar(100);uniqueIndex;not null"`
    PasswordHash string     `gorm:"type:varchar(255);not null"`
    FirstName    string     `gorm:"type:varchar(50)"`
    LastName     string     `gorm:"type:varchar(50)"`
    Avatar       string     `gorm:"type:varchar(255)"`
    Phone        string     `gorm:"type:varchar(20)"`
    Status       string     `gorm:"type:varchar(20);not null;default:active"`
    CreatedAt    time.Time  `gorm:"not null"`
    UpdatedAt    time.Time  `gorm:"not null"`
    DeletedAt    gorm.DeletedAt `gorm:"index"`
    
    // 关联
    Orders []OrderModel `gorm:"foreignKey:UserID"`
}

// TableName 表名
func (UserModel) TableName() string {
    return "users"
}

// BeforeCreate 创建前钩子
func (u *UserModel) BeforeCreate(tx *gorm.DB) error {
    if u.ID == uuid.Nil {
        u.ID = uuid.New()
    }
    return nil
}

// OrderModel 订单模型
type OrderModel struct {
    ID          uuid.UUID `gorm:"type:uuid;primary_key"`
    UserID      uuid.UUID `gorm:"type:uuid;index;not null"`
    OrderNumber string    `gorm:"type:varchar(50);uniqueIndex;not null"`
    TotalAmount float64   `gorm:"type:decimal(10,2);not null"`
    Status      string    `gorm:"type:varchar(20);not null"`
    CreatedAt   time.Time `gorm:"not null"`
    UpdatedAt   time.Time `gorm:"not null"`
    
    // 关联
    User UserModel `gorm:"foreignKey:UserID"`
}

func (OrderModel) TableName() string {
    return "orders"
}

4.2 仓储实现

// internal/infrastructure/persistence/user_repository.go
package persistence

import (
    "context"
    "fmt"
    
    "gorm.io/gorm"
    "github.com/google/uuid"
    
    "ecommerce-api/internal/domain/entity"
    "ecommerce-api/internal/domain/repository"
    "ecommerce-api/internal/infrastructure/persistence/model"
    "ecommerce-api/internal/infrastructure/persistence/mapper"
)

// UserRepositoryImpl 用户仓储实现
type UserRepositoryImpl struct {
    db *gorm.DB
}

func NewUserRepository(db *gorm.DB) repository.UserRepository {
    return &UserRepositoryImpl{db: db}
}

func (r *UserRepositoryImpl) Save(ctx context.Context, user *entity.User) error {
    userModel := mapper.UserToModel(user)
    
    if err := r.db.WithContext(ctx).Create(userModel).Error; err != nil {
        return fmt.Errorf("failed to save user: %w", err)
    }
    
    return nil
}

func (r *UserRepositoryImpl) Update(ctx context.Context, user *entity.User) error {
    userModel := mapper.UserToModel(user)
    
    if err := r.db.WithContext(ctx).Save(userModel).Error; err != nil {
        return fmt.Errorf("failed to update user: %w", err)
    }
    
    return nil
}

func (r *UserRepositoryImpl) Delete(ctx context.Context, userID string) error {
    id, err := uuid.Parse(userID)
    if err != nil {
        return fmt.Errorf("invalid user ID: %w", err)
    }
    
    if err := r.db.WithContext(ctx).Delete(&model.UserModel{}, "id = ?", id).Error; err != nil {
        return fmt.Errorf("failed to delete user: %w", err)
    }
    
    return nil
}

func (r *UserRepositoryImpl) FindByID(ctx context.Context, userID string) (*entity.User, error) {
    id, err := uuid.Parse(userID)
    if err != nil {
        return nil, fmt.Errorf("invalid user ID: %w", err)
    }
    
    var userModel model.UserModel
    if err := r.db.WithContext(ctx).First(&userModel, "id = ?", id).Error; err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {
            return nil, nil
        }
        return nil, fmt.Errorf("failed to find user by ID: %w", err)
    }
    
    return mapper.ModelToUser(&userModel), nil
}

func (r *UserRepositoryImpl) FindByUsername(ctx context.Context, username string) (*entity.User, error) {
    var userModel model.UserModel
    if err := r.db.WithContext(ctx).First(&userModel, "username = ?", username).Error; err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {
            return nil, nil
        }
        return nil, fmt.Errorf("failed to find user by username: %w", err)
    }
    
    return mapper.ModelToUser(&userModel), nil
}

func (r *UserRepositoryImpl) FindByEmail(ctx context.Context, email string) (*entity.User, error) {
    var userModel model.UserModel
    if err := r.db.WithContext(ctx).First(&userModel, "email = ?", email).Error; err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {
            return nil, nil
        }
        return nil, fmt.Errorf("failed to find user by email: %w", err)
    }
    
    return mapper.ModelToUser(&userModel), nil
}

func (r *UserRepositoryImpl) FindAll(ctx context.Context, limit, offset int) ([]*entity.User, error) {
    var userModels []model.UserModel
    if err := r.db.WithContext(ctx).Limit(limit).Offset(offset).Find(&userModels).Error; err != nil {
        return nil, fmt.Errorf("failed to find all users: %w", err)
    }
    
    users := make([]*entity.User, len(userModels))
    for i, userModel := range userModels {
        users[i] = mapper.ModelToUser(&userModel)
    }
    
    return users, nil
}

func (r *UserRepositoryImpl) FindByStatus(ctx context.Context, status entity.UserStatus, limit, offset int) ([]*entity.User, error) {
    var userModels []model.UserModel
    if err := r.db.WithContext(ctx).
        Where("status = ?", string(status)).
        Limit(limit).
        Offset(offset).
        Find(&userModels).Error; err != nil {
        return nil, fmt.Errorf("failed to find users by status: %w", err)
    }
    
    users := make([]*entity.User, len(userModels))
    for i, userModel := range userModels {
        users[i] = mapper.ModelToUser(&userModel)
    }
    
    return users, nil
}

func (r *UserRepositoryImpl) FindByIDWithOrders(ctx context.Context, userID string) (*entity.User, error) {
    id, err := uuid.Parse(userID)
    if err != nil {
        return nil, fmt.Errorf("invalid user ID: %w", err)
    }
    
    var userModel model.UserModel
    if err := r.db.WithContext(ctx).
        Preload("Orders").
        First(&userModel, "id = ?", id).Error; err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {
            return nil, nil
        }
        return nil, fmt.Errorf("failed to find user with orders: %w", err)
    }
    
    return mapper.ModelToUserWithOrders(&userModel), nil
}

func (r *UserRepositoryImpl) Count(ctx context.Context) (int64, error) {
    var count int64
    if err := r.db.WithContext(ctx).Model(&model.UserModel{}).Count(&count).Error; err != nil {
        return 0, fmt.Errorf("failed to count users: %w", err)
    }
    
    return count, nil
}

func (r *UserRepositoryImpl) CountByStatus(ctx context.Context, status entity.UserStatus) (int64, error) {
    var count int64
    if err := r.db.WithContext(ctx).
        Model(&model.UserModel{}).
        Where("status = ?", string(status)).
        Count(&count).Error; err != nil {
        return 0, fmt.Errorf("failed to count users by status: %w", err)
    }
    
    return count, nil
}

func (r *UserRepositoryImpl) WithTx(ctx context.Context, fn func(ctx context.Context) error) error {
    return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
        txRepo := &UserRepositoryImpl{db: tx}
        ctx = context.WithValue(ctx, "tx_repo", txRepo)
        return fn(ctx)
    })
}

4.3 数据库配置

// internal/infrastructure/config/database.go
package config

import (
    "fmt"
    "time"
    
    "gorm.io/driver/mysql"
    "gorm.io/driver/postgres"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    
    "ecommerce-api/internal/infrastructure/logger"
)

// DatabaseConfig 数据库配置
type DatabaseConfig struct {
    Driver   string `yaml:"driver" validate:"required,oneof=mysql postgres sqlite"`
    Host     string `yaml:"host" validate:"required"`
    Port     int    `yaml:"port" validate:"required"`
    Username string `yaml:"username" validate:"required"`
    Password string `yaml:"password" validate:"required"`
    Database string `yaml:"database" validate:"required"`
    Charset  string `yaml:"charset" default:"utf8mb4"`
    
    // 连接池配置
    MaxIdleConns    int           `yaml:"max_idle_conns" default:"10"`
    MaxOpenConns    int           `yaml:"max_open_conns" default:"100"`
    ConnMaxLifetime time.Duration `yaml:"conn_max_lifetime" default:"1h"`
    ConnMaxIdleTime time.Duration `yaml:"conn_max_idle_time" default:"10m"`
    
    // 日志配置
    LogLevel string `yaml:"log_level" default:"info"`
}

// NewDatabaseConnection 创建数据库连接
func NewDatabaseConnection(cfg DatabaseConfig, appLogger logger.Logger) (*gorm.DB, error) {
    var dialector gorm.Dialector
    
    switch cfg.Driver {
    case "mysql":
        dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
            cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.Database, cfg.Charset)
        dialector = mysql.Open(dsn)
    case "postgres":
        dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
            cfg.Host, cfg.Port, cfg.Username, cfg.Password, cfg.Database)
        dialector = postgres.Open(dsn)
    case "sqlite":
        dialector = sqlite.Open(cfg.Database)
    default:
        return nil, fmt.Errorf("unsupported database driver: %s", cfg.Driver)
    }
    
    // 配置GORM日志
    logLevel := logger.Silent
    switch cfg.LogLevel {
    case "error":
        logLevel = logger.Error
    case "warn":
        logLevel = logger.Warn
    case "info":
        logLevel = logger.Info
    }
    
    gormLogger := logger.New(
        appLogger,
        logger.Config{
            SlowThreshold:             time.Second,
            LogLevel:                  logLevel,
            IgnoreRecordNotFoundError: true,
            Colorful:                  true,
        },
    )
    
    // GORM配置
    config := &gorm.Config{
        Logger: gormLogger,
        NowFunc: func() time.Time {
            return time.Now().UTC()
        },
        PrepareStmt: true,
    }
    
    // 创建连接
    db, err := gorm.Open(dialector, config)
    if err != nil {
        return nil, fmt.Errorf("failed to connect to database: %w", err)
    }
    
    // 获取底层SQL数据库连接
    sqlDB, err := db.DB()
    if err != nil {
        return nil, fmt.Errorf("failed to get database instance: %w", err)
    }
    
    // 设置连接池参数
    sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
    sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
    sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifetime)
    sqlDB.SetConnMaxIdleTime(cfg.ConnMaxIdleTime)
    
    // 测试连接
    if err := sqlDB.Ping(); err != nil {
        return nil, fmt.Errorf("failed to ping database: %w", err)
    }
    
    appLogger.Info("Database connection established", "driver", cfg.Driver, "host", cfg.Host)
    
    return db, nil
}

// AutoMigrate 自动迁移数据库
func AutoMigrate(db *gorm.DB) error {
    return db.AutoMigrate(
        &model.UserModel{},
        &model.OrderModel{},
        // 其他模型...
    )
}

5. 接口层实现

5.1 HTTP处理器

// internal/interface/http/handler/user_handler.go
package httphandler

import (
    "net/http"
    "strconv"
    
    "github.com/gin-gonic/gin"
    "github.com/google/uuid"
    
    "ecommerce-api/internal/application/dto"
    "ecommerce-api/internal/application/usecase"
    "ecommerce-api/internal/infrastructure/response"
    "ecommerce-api/internal/infrastructure/validator"
)

// UserHandler 用户HTTP处理器
type UserHandler struct {
    userUseCase *usecase.UserUseCase
    validator   *validator.Validator
}

func NewUserHandler(userUseCase *usecase.UserUseCase, validator *validator.Validator) *UserHandler {
    return &UserHandler{
        userUseCase: userUseCase,
        validator:   validator,
    }
}

// CreateUser 创建用户
// @Summary Create a new user
// @Description Create a new user with the provided information
// @Tags users
// @Accept json
// @Produce json
// @Param user body dto.CreateUserDTO true "User information"
// @Success 201 {object} response.Response{data=dto.UserResponseDTO}
// @Failure 400 {object} response.Response
// @Failure 409 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /users [post]
func (h *UserHandler) CreateUser(c *gin.Context) {
    var req dto.CreateUserDTO
    if err := c.ShouldBindJSON(&req); err != nil {
        response.Error(c, http.StatusBadRequest, "invalid_request", "Invalid request body", err.Error())
        return
    }
    
    // 验证请求
    if err := h.validator.Validate(req); err != nil {
        response.ValidationError(c, err)
        return
    }
    
    // 调用用例
    user, err := h.userUseCase.CreateUser(c.Request.Context(), req)
    if err != nil {
        switch err.Error() {
        case "username already exists":
            response.Error(c, http.StatusConflict, "username_exists", "Username already exists", nil)
        case "email already exists":
            response.Error(c, http.StatusConflict, "email_exists", "Email already exists", nil)
        default:
            response.Error(c, http.StatusInternalServerError, "internal_error", "Failed to create user", err.Error())
        }
        return
    }
    
    response.Success(c, http.StatusCreated, "User created successfully", user)
}

// GetUser 获取用户信息
// @Summary Get user by ID
// @Description Get user information by user ID
// @Tags users
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Success 200 {object} response.Response{data=dto.UserResponseDTO}
// @Failure 400 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /users/{id} [get]
func (h *UserHandler) GetUser(c *gin.Context) {
    userID := c.Param("id")
    
    // 验证UUID格式
    if _, err := uuid.Parse(userID); err != nil {
        response.Error(c, http.StatusBadRequest, "invalid_user_id", "Invalid user ID format", nil)
        return
    }
    
    user, err := h.userUseCase.GetUser(c.Request.Context(), userID)
    if err != nil {
        if err.Error() == "user not found" {
            response.Error(c, http.StatusNotFound, "user_not_found", "User not found", nil)
        } else {
            response.Error(c, http.StatusInternalServerError, "internal_error", "Failed to get user", err.Error())
        }
        return
    }
    
    response.Success(c, http.StatusOK, "User retrieved successfully", user)
}

// UpdateUser 更新用户信息
// @Summary Update user
// @Description Update user information
// @Tags users
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Param user body dto.UpdateUserDTO true "User information"
// @Success 200 {object} response.Response{data=dto.UserResponseDTO}
// @Failure 400 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /users/{id} [put]
func (h *UserHandler) UpdateUser(c *gin.Context) {
    userID := c.Param("id")
    
    // 验证UUID格式
    if _, err := uuid.Parse(userID); err != nil {
        response.Error(c, http.StatusBadRequest, "invalid_user_id", "Invalid user ID format", nil)
        return
    }
    
    var req dto.UpdateUserDTO
    if err := c.ShouldBindJSON(&req); err != nil {
        response.Error(c, http.StatusBadRequest, "invalid_request", "Invalid request body", err.Error())
        return
    }
    
    // 设置用户ID
    req.UserID = userID
    
    // 验证请求
    if err := h.validator.Validate(req); err != nil {
        response.ValidationError(c, err)
        return
    }
    
    user, err := h.userUseCase.UpdateUser(c.Request.Context(), req)
    if err != nil {
        switch err.Error() {
        case "user not found":
            response.Error(c, http.StatusNotFound, "user_not_found", "User not found", nil)
        case "username already exists":
            response.Error(c, http.StatusConflict, "username_exists", "Username already exists", nil)
        case "email already exists":
            response.Error(c, http.StatusConflict, "email_exists", "Email already exists", nil)
        default:
            response.Error(c, http.StatusInternalServerError, "internal_error", "Failed to update user", err.Error())
        }
        return
    }
    
    response.Success(c, http.StatusOK, "User updated successfully", user)
}

// DeleteUser 删除用户
// @Summary Delete user
// @Description Delete a user by ID
// @Tags users
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /users/{id} [delete]
func (h *UserHandler) DeleteUser(c *gin.Context) {
    userID := c.Param("id")
    
    // 验证UUID格式
    if _, err := uuid.Parse(userID); err != nil {
        response.Error(c, http.StatusBadRequest, "invalid_user_id", "Invalid user ID format", nil)
        return
    }
    
    err := h.userUseCase.DeleteUser(c.Request.Context(), userID)
    if err != nil {
        switch err.Error() {
        case "user not found":
            response.Error(c, http.StatusNotFound, "user_not_found", "User not found", nil)
        case "cannot delete user with existing orders":
            response.Error(c, http.StatusConflict, "user_has_orders", "Cannot delete user with existing orders", nil)
        default:
            response.Error(c, http.StatusInternalServerError, "internal_error", "Failed to delete user", err.Error())
        }
        return
    }
    
    response.Success(c, http.StatusOK, "User deleted successfully", nil)
}

// ListUsers 获取用户列表
// @Summary List users
// @Description Get a list of users with pagination
// @Tags users
// @Accept json
// @Produce json
// @Param page query int false "Page number" default(1)
// @Param page_size query int false "Page size" default(10)
// @Param status query string false "User status" Enums(active,inactive,banned)
// @Param search query string false "Search keyword"
// @Success 200 {object} response.Response{data=dto.ListUsersResponseDTO}
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /users [get]
func (h *UserHandler) ListUsers(c *gin.Context) {
    page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
    pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
    status := c.Query("status")
    search := c.Query("search")
    
    req := dto.ListUsersDTO{
        Page:     page,
        PageSize: pageSize,
        Status:   status,
        Search:   search,
    }
    
    // 验证请求
    if err := h.validator.Validate(req); err != nil {
        response.ValidationError(c, err)
        return
    }
    
    result, err := h.userUseCase.ListUsers(c.Request.Context(), req)
    if err != nil {
        response.Error(c, http.StatusInternalServerError, "internal_error", "Failed to list users", err.Error())
        return
    }
    
    response.Success(c, http.StatusOK, "Users retrieved successfully", result)
}

5.2 响应封装

// internal/infrastructure/response/response.go
package response

import (
    "net/http"
    "time"
    
    "github.com/gin-gonic/gin"
)

// Response 统一响应格式
type Response struct {
    Success   bool        `json:"success"`
    Code      string      `json:"code"`
    Message   string      `json:"message"`
    Data      interface{} `json:"data,omitempty"`
    Timestamp time.Time   `json:"timestamp"`
    Path      string      `json:"path,omitempty"`
    Method    string      `json:"method,omitempty"`
}

// Success 成功响应
func Success(c *gin.Context, statusCode int, message string, data interface{}) {
    c.JSON(statusCode, Response{
        Success:   true,
        Code:      "success",
        Message:   message,
        Data:      data,
        Timestamp: time.Now(),
        Path:      c.Request.URL.Path,
        Method:    c.Request.Method,
    })
}

// Error 错误响应
func Error(c *gin.Context, statusCode int, code, message string, details interface{}) {
    c.JSON(statusCode, Response{
        Success:   false,
        Code:      code,
        Message:   message,
        Data:      details,
        Timestamp: time.Now(),
        Path:      c.Request.URL.Path,
        Method:    c.Request.Method,
    })
}

// ValidationError 验证错误响应
func ValidationError(c *gin.Context, errors interface{}) {
    Error(c, http.StatusBadRequest, "validation_error", "Validation failed", errors)
}

// NotFound 404响应
func NotFound(c *gin.Context, message string) {
    Error(c, http.StatusNotFound, "not_found", message, nil)
}

// Unauthorized 401响应
func Unauthorized(c *gin.Context, message string) {
    Error(c, http.StatusUnauthorized, "unauthorized", message, nil)
}

// Forbidden 403响应
func Forbidden(c *gin.Context, message string) {
    Error(c, http.StatusForbidden, "forbidden", message, nil)
}

// InternalError 500响应
func InternalError(c *gin.Context, message string, details interface{}) {
    Error(c, http.StatusInternalServerError, "internal_error", message, details)
}

// BadRequest 400响应
func BadRequest(c *gin.Context, message string, details interface{}) {
    Error(c, http.StatusBadRequest, "bad_request", message, details)
}

6. 测试实现

6.1 单元测试

// internal/application/usecase/user_usecase_test.go
package usecase_test

import (
    "context"
    "testing"
    
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/mock"
    "github.com/google/uuid"
    
    "ecommerce-api/internal/application/dto"
    "ecommerce-api/internal/application/usecase"
    "ecommerce-api/internal/domain/entity"
    "ecommerce-api/internal/domain/service"
    "ecommerce-api/mocks"
)

// MockUserService 模拟用户服务
type MockUserService struct {
    mock.Mock
}

func (m *MockUserService) RegisterUser(ctx context.Context, username, email, password string) (*entity.User, error) {
    args := m.Called(ctx, username, email, password)
    if args.Get(0) == nil {
        return nil, args.Error(1)
    }
    return args.Get(0).(*entity.User), args.Error(1)
}

func (m *MockUserService) UpdateUserProfile(ctx context.Context, userID string, profile *entity.UserProfile) error {
    args := m.Called(ctx, userID, profile)
    return args.Error(0)
}

func (m *MockUserService) ValidateCredentials(ctx context.Context, username, password string) (*entity.User, error) {
    args := m.Called(ctx, username, password)
    if args.Get(0) == nil {
        return nil, args.Error(1)
    }
    return args.Get(0).(*entity.User), args.Error(1)
}

// MockUserRepository 模拟用户仓储
type MockUserRepository struct {
    mock.Mock
}

func (m *MockUserRepository) Save(ctx context.Context, user *entity.User) error {
    args := m.Called(ctx, user)
    return args.Error(0)
}

func (m *MockUserRepository) Update(ctx context.Context, user *entity.User) error {
    args := m.Called(ctx, user)
    return args.Error(0)
}

func (m *MockUserRepository) FindByID(ctx context.Context, userID string) (*entity.User, error) {
    args := m.Called(ctx, userID)
    if args.Get(0) == nil {
        return nil, args.Error(1)
    }
    return args.Get(0).(*entity.User), args.Error(1)
}

func TestUserUseCase_CreateUser(t *testing.T) {
    tests := []struct {
        name          string
        input         dto.CreateUserDTO
        mockSetup     func(*MockUserService, *MockUserRepository)
        expectedError bool
        expectedUser  *dto.UserResponseDTO
    }{
        {
            name: "successful user creation",
            input: dto.CreateUserDTO{
                Username:  "testuser",
                Email:     "test@example.com",
                Password:  "Test123!@#",
                FirstName: "Test",
                LastName:  "User",
            },
            mockSetup: func(userService *MockUserService, userRepo *MockUserRepository) {
                user := &entity.User{}
                userService.On("RegisterUser", mock.Anything, "testuser", "test@example.com", "Test123!@#").
                    Return(user, nil)
                userService.On("UpdateUserProfile", mock.Anything, mock.Anything, mock.Anything).
                    Return(nil)
            },
            expectedError: false,
        },
        {
            name: "username already exists",
            input: dto.CreateUserDTO{
                Username: "existinguser",
                Email:    "test@example.com",
                Password: "Test123!@#",
            },
            mockSetup: func(userService *MockUserService, userRepo *MockUserRepository) {
                userService.On("RegisterUser", mock.Anything, "existinguser", "test@example.com", "Test123!@#").
                    Return(nil, errors.New("username already exists"))
            },
            expectedError: true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // 设置模拟对象
            userService := new(MockUserService)
            userRepo := new(MockUserRepository)
            logger := mocks.NewMockLogger()
            
            tt.mockSetup(userService, userRepo)
            
            // 创建用例
            useCase := usecase.NewUserUseCase(userService, userRepo, logger)
            
            // 执行测试
            result, err := useCase.CreateUser(context.Background(), tt.input)
            
            // 验证结果
            if tt.expectedError {
                assert.Error(t, err)
                assert.Nil(t, result)
            } else {
                assert.NoError(t, err)
                assert.NotNil(t, result)
            }
            
            // 验证模拟调用
            userService.AssertExpectations(t)
            userRepo.AssertExpectations(t)
        })
    }
}

func TestUserUseCase_GetUser(t *testing.T) {
    tests := []struct {
        name          string
        userID        string
        mockSetup     func(*MockUserRepository)
        expectedError bool
    }{
        {
            name:   "user found",
            userID: uuid.New().String(),
            mockSetup: func(userRepo *MockUserRepository) {
                user := &entity.User{}
                userRepo.On("FindByID", mock.Anything, mock.Anything).Return(user, nil)
            },
            expectedError: false,
        },
        {
            name:   "user not found",
            userID: uuid.New().String(),
            mockSetup: func(userRepo *MockUserRepository) {
                userRepo.On("FindByID", mock.Anything, mock.Anything).Return(nil, nil)
            },
            expectedError: true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            userService := new(MockUserService)
            userRepo := new(MockUserRepository)
            logger := mocks.NewMockLogger()
            
            tt.mockSetup(userRepo)
            
            useCase := usecase.NewUserUseCase(userService, userRepo, logger)
            
            result, err := useCase.GetUser(context.Background(), tt.userID)
            
            if tt.expectedError {
                assert.Error(t, err)
                assert.Nil(t, result)
            } else {
                assert.NoError(t, err)
                assert.NotNil(t, result)
            }
            
            userRepo.AssertExpectations(t)
        })
    }
}

func TestUserUseCase_ListUsers(t *testing.T) {
    // Table-driven test for ListUsers
    tests := []struct {
        name          string
        input         dto.ListUsersDTO
        mockSetup     func(*MockUserRepository)
        expectedCount int
        expectedError bool
    }{
        {
            name: "list active users",
            input: dto.ListUsersDTO{
                Page:     1,
                PageSize: 10,
                Status:   "active",
            },
            mockSetup: func(userRepo *MockUserRepository) {
                users := []*entity.User{
                    &entity.User{},
                    &entity.User{},
                }
                userRepo.On("FindByStatus", mock.Anything, entity.UserStatusActive, 10, 0).
                    Return(users, nil)
                userRepo.On("CountByStatus", mock.Anything, entity.UserStatusActive).
                    Return(int64(2), nil)
            },
            expectedCount: 2,
            expectedError: false,
        },
        {
            name: "list all users",
            input: dto.ListUsersDTO{
                Page:     1,
                PageSize: 10,
            },
            mockSetup: func(userRepo *MockUserRepository) {
                users := []*entity.User{
                    &entity.User{},
                    &entity.User{},
                    &entity.User{},
                }
                userRepo.On("FindAll", mock.Anything, 10, 0).
                    Return(users, nil)
                userRepo.On("Count", mock.Anything).
                    Return(int64(3), nil)
            },
            expectedCount: 3,
            expectedError: false,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            userService := new(MockUserService)
            userRepo := new(MockUserRepository)
            logger := mocks.NewMockLogger()
            
            tt.mockSetup(userRepo)
            
            useCase := usecase.NewUserUseCase(userService, userRepo, logger)
            
            result, err := useCase.ListUsers(context.Background(), tt.input)
            
            if tt.expectedError {
                assert.Error(t, err)
                assert.Nil(t, result)
            } else {
                assert.NoError(t, err)
                assert.NotNil(t, result)
                assert.Equal(t, tt.expectedCount, len(result.Users))
                assert.Equal(t, int64(tt.expectedCount), result.Total)
            }
            
            userRepo.AssertExpectations(t)
        })
    }
}

6.2 集成测试

// tests/integration/user_integration_test.go
package integration

import (
    "bytes"
    "context"
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "testing"
    
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/suite"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    
    "ecommerce-api/internal/application/dto"
    "ecommerce-api/internal/infrastructure/config"
    "ecommerce-api/internal/infrastructure/persistence"
    "ecommerce-api/internal/interface/http"
)

// UserIntegrationTestSuite 用户集成测试套件
type UserIntegrationTestSuite struct {
    suite.Suite
    db       *gorm.DB
    router   *http.ServeMux
    server   *httptest.Server
}

func (suite *UserIntegrationTestSuite) SetupSuite() {
    // 创建测试数据库
    db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
    suite.Require().NoError(err)
    
    // 运行迁移
    err = db.AutoMigrate(&persistence.UserModel{})
    suite.Require().NoError(err)
    
    suite.db = db
    
    // 创建HTTP服务器
    cfg := &config.Config{
        Server: config.ServerConfig{
            Host: "localhost",
            Port: "8080",
        },
        Database: config.DatabaseConfig{
            Driver: "sqlite",
        },
    }
    
    // 初始化依赖
    userRepo := persistence.NewUserRepository(db)
    // ... 初始化其他依赖
    
    // 创建路由
    router := http.NewServeMux()
    httphandler.RegisterRoutes(router, cfg, userRepo /* 其他依赖 */)
    
    suite.router = router
    suite.server = httptest.NewServer(router)
}

func (suite *UserIntegrationTestSuite) TearDownSuite() {
    suite.server.Close()
    
    sqlDB, err := suite.db.DB()
    suite.Require().NoError(err)
    sqlDB.Close()
}

func (suite *UserIntegrationTestSuite) TestCreateUser() {
    // 测试创建用户
    createUserReq := dto.CreateUserDTO{
        Username:  "integration_test_user",
        Email:     "integration@test.com",
        Password:  "Test123!@#",
        FirstName: "Integration",
        LastName:  "Test",
    }
    
    body, err := json.Marshal(createUserReq)
    suite.Require().NoError(err)
    
    resp, err := http.Post(suite.server.URL+"/api/v1/users", "application/json", bytes.NewBuffer(body))
    suite.Require().NoError(err)
    defer resp.Body.Close()
    
    assert.Equal(suite.T(), http.StatusCreated, resp.StatusCode)
    
    var result map[string]interface{}
    err = json.NewDecoder(resp.Body).Decode(&result)
    suite.Require().NoError(err)
    
    assert.True(suite.T(), result["success"].(bool))
    assert.NotNil(suite.T(), result["data"])
}

func (suite *UserIntegrationTestSuite) TestGetUser() {
    // 先创建用户
    createUserReq := dto.CreateUserDTO{
        Username: "get_test_user",
        Email:    "get@test.com",
        Password: "Test123!@#",
    }
    
    body, _ := json.Marshal(createUserReq)
    createResp, _ := http.Post(suite.server.URL+"/api/v1/users", "application/json", bytes.NewBuffer(body))
    createResp.Body.Close()
    
    // 解析创建响应获取用户ID
    var createResult map[string]interface{}
    json.NewDecoder(createResp.Body).Decode(&createResult)
    userData := createResult["data"].(map[string]interface{})
    userID := userData["user_id"].(string)
    
    // 测试获取用户
    resp, err := http.Get(suite.server.URL + "/api/v1/users/" + userID)
    suite.Require().NoError(err)
    defer resp.Body.Close()
    
    assert.Equal(suite.T(), http.StatusOK, resp.StatusCode)
    
    var result map[string]interface{}
    err = json.NewDecoder(resp.Body).Decode(&result)
    suite.Require().NoError(err)
    
    assert.True(suite.T(), result["success"].(bool))
    assert.NotNil(suite.T(), result["data"])
}

func (suite *UserIntegrationTestSuite) TestListUsers() {
    // 创建多个用户
    for i := 0; i < 5; i++ {
        createUserReq := dto.CreateUserDTO{
            Username: fmt.Sprintf("list_test_user_%d", i),
            Email:    fmt.Sprintf("list%d@test.com", i),
            Password: "Test123!@#",
        }
        
        body, _ := json.Marshal(createUserReq)
        resp, _ := http.Post(suite.server.URL+"/api/v1/users", "application/json", bytes.NewBuffer(body))
        resp.Body.Close()
    }
    
    // 测试获取用户列表
    resp, err := http.Get(suite.server.URL + "/api/v1/users?page=1&page_size=10")
    suite.Require().NoError(err)
    defer resp.Body.Close()
    
    assert.Equal(suite.T(), http.StatusOK, resp.StatusCode)
    
    var result map[string]interface{}
    err = json.NewDecoder(resp.Body).Decode(&result)
    suite.Require().NoError(err)
    
    assert.True(suite.T(), result["success"].(bool))
    data := result["data"].(map[string]interface{})
    assert.GreaterOrEqual(suite.T(), data["total"], float64(5))
    assert.NotNil(suite.T(), data["users"])
}

func TestUserIntegrationTestSuite(t *testing.T) {
    suite.Run(t, new(UserIntegrationTestSuite))
}

7. Docker化部署

7.1 Dockerfile

# Dockerfile
# 构建阶段
FROM golang:1.21-alpine AS builder

# 安装构建依赖
RUN apk add --no-cache git ca-certificates tzdata

# 设置工作目录
WORKDIR /app

# 复制go mod文件
COPY go.mod go.sum ./
RUN go mod download

# 复制源代码
COPY . .

# 构建应用
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main cmd/api/main.go

# 运行阶段
FROM alpine:latest

# 安装运行时依赖
RUN apk --no-cache add ca-certificates

# 创建非root用户
RUN addgroup -g 1000 -S appgroup && \
    adduser -u 1000 -S appuser -G appgroup

# 设置工作目录
WORKDIR /root/

# 从构建阶段复制二进制文件
COPY --from=builder /app/main .

# 复制配置文件
COPY --from=builder /app/config ./config

# 复制迁移文件
COPY --from=builder /app/migrations ./migrations

# 设置时区
ENV TZ=Asia/Shanghai

# 切换到非root用户
USER appuser

# 暴露端口
EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1

# 启动命令
CMD ["./main"]

7.2 Docker Compose配置

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ecommerce-api
    ports:
      - "8080:8080"
    environment:
      - APP_ENV=production
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USER=ecommerce
      - DB_PASSWORD=ecommerce123
      - DB_NAME=ecommerce_db
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      - postgres
      - redis
    volumes:
      - ./config:/root/config
      - ./logs:/root/logs
    networks:
      - ecommerce-network
    restart: unless-stopped

  postgres:
    image: postgres:15-alpine
    container_name: ecommerce-postgres
    environment:
      - POSTGRES_DB=ecommerce_db
      - POSTGRES_USER=ecommerce
      - POSTGRES_PASSWORD=ecommerce123
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./migrations:/docker-entrypoint-initdb.d
    networks:
      - ecommerce-network
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    container_name: ecommerce-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - ecommerce-network
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    container_name: ecommerce-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl:/etc/nginx/ssl
      - ./logs/nginx:/var/log/nginx
    depends_on:
      - app
    networks:
      - ecommerce-network
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

networks:
  ecommerce-network:
    driver: bridge

7.3 生产环境配置

# docker-compose.prod.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
    environment:
      - APP_ENV=production
      - LOG_LEVEL=info
      - DB_MAX_OPEN_CONNS=25
      - DB_MAX_IDLE_CONNS=5
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=ecommerce_db
      - POSTGRES_USER=ecommerce
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

secrets:
  db_password:
    external: true

configs:
  nginx_config:
    external: true

8. Makefile构建脚本

# Makefile
.PHONY: help build run test clean docker-build docker-run migrate lint

# 变量定义
APP_NAME=ecommerce-api
DOCKER_IMAGE=$(APP_NAME):latest
GO_VERSION=1.21

# 帮助信息
help:
	@echo "Available targets:"
	@echo "  help        - Show this help message"
	@echo "  build       - Build the application"
	@echo "  run         - Run the application"
	@echo "  test        - Run tests"
	@echo "  test-coverage - Run tests with coverage"
	@echo "  lint        - Run linter"
	@echo "  migrate     - Run database migrations"
	@echo "  docker-build - Build Docker image"
	@echo "  docker-run  - Run Docker container"
	@echo "  clean       - Clean build artifacts"

# 构建应用
build:
	@echo "Building $(APP_NAME)..."
	@go build -o bin/$(APP_NAME) cmd/api/main.go
	@echo "Build complete: bin/$(APP_NAME)"

# 运行应用
run:
	@echo "Running $(APP_NAME)..."
	@go run cmd/api/main.go

# 运行测试
test:
	@echo "Running tests..."
	@go test -v ./...

# 运行测试并生成覆盖率报告
test-coverage:
	@echo "Running tests with coverage..."
	@go test -v -coverprofile=coverage.out ./...
	@go tool cover -html=coverage.out -o coverage.html
	@echo "Coverage report generated: coverage.html"

# 运行基准测试
benchmark:
	@echo "Running benchmarks..."
	@go test -bench=. -benchmem ./...

# 代码格式化
fmt:
	@echo "Formatting code..."
	@go fmt ./...

# 运行静态代码分析
lint:
	@echo "Running linter..."
	@golangci-lint run

# 下载依赖
deps:
	@echo "Downloading dependencies..."
	@go mod download
	@go mod tidy

# 运行数据库迁移
migrate:
	@echo "Running database migrations..."
	@go run cmd/migrate/main.go

# 创建新的迁移文件
migrate-create:
	@echo "Creating new migration..."
	@read -p "Enter migration name: " name; \
	go run cmd/migrate/main.go create $$name

# 构建Docker镜像
docker-build:
	@echo "Building Docker image..."
	@docker build -t $(DOCKER_IMAGE) .
	@echo "Docker image built: $(DOCKER_IMAGE)"

# 运行Docker容器
docker-run:
	@echo "Running Docker container..."
	@docker-compose up -d

# 停止Docker容器
docker-stop:
	@echo "Stopping Docker containers..."
	@docker-compose down

# 查看Docker日志
docker-logs:
	@docker-compose logs -f

# 清理构建产物
clean:
	@echo "Cleaning build artifacts..."
	@rm -rf bin/
	@rm -f coverage.out coverage.html
	@docker system prune -f
	@echo "Clean complete"

# 安装开发工具
install-tools:
	@echo "Installing development tools..."
	@go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
	@go install github.com/swaggo/swag/cmd/swag@latest
	@go install github.com/golang/mock/mockgen@latest
	@echo "Development tools installed"

# 生成Swagger文档
swagger:
	@echo "Generating Swagger documentation..."
	@swag init -g cmd/api/main.go
	@echo "Swagger documentation generated"

# 生成Mock代码
generate-mocks:
	@echo "Generating mock code..."
	@go generate ./...
	@echo "Mock code generated"

# 运行完整CI流程
ci: fmt lint test build
	@echo "CI pipeline completed successfully"

# 部署到生产环境
deploy:
	@echo "Deploying to production..."
	@docker-compose -f docker-compose.prod.yml up -d
	@echo "Deployment complete"

# 回滚部署
rollback:
	@echo "Rolling back deployment..."
	@docker-compose -f docker-compose.prod.yml down
	@docker-compose -f docker-compose.prod.yml up -d
	@echo "Rollback complete"

9. 部署和运维

9.1 环境配置

# config/config.yaml
app:
  name: ecommerce-api
  version: 1.0.0
  env: development # development, staging, production

server:
  host: 0.0.0.0
  port: 8080
  read_timeout: 30s
  write_timeout: 30s
  idle_timeout: 60s
  max_header_bytes: 1048576 # 1MB

database:
  driver: postgres # mysql, postgres, sqlite
  host: localhost
  port: 5432
  username: ecommerce
  password: ecommerce123
  database: ecommerce_db
  charset: utf8mb4
  max_idle_conns: 10
  max_open_conns: 100
  conn_max_lifetime: 1h
  conn_max_idle_time: 10m
  log_level: info # error, warn, info, debug

redis:
  host: localhost
  port: 6379
  password: ""
  db: 0
  pool_size: 10
  min_idle_conns: 5
  max_retries: 3
  dial_timeout: 5s
  read_timeout: 3s
  write_timeout: 3s

jwt:
  secret: your-secret-key-here
  access_token_expire: 24h
  refresh_token_expire: 168h # 7 days

log:
  level: info # debug, info, warn, error
  format: json # json, text
  output: stdout # stdout, file
  file_path: /var/log/ecommerce-api.log
  max_size: 100 # MB
  max_backups: 5
  max_age: 30 # days
  compress: true

metrics:
  enabled: true
  path: /metrics
  namespace: ecommerce_api

tracing:
  enabled: true
  jaeger_endpoint: http://localhost:14268/api/traces
  sample_rate: 1.0

security:
  cors:
    enabled: true
    allowed_origins:
      - http://localhost:3000
      - https://example.com
    allowed_methods:
      - GET
      - POST
      - PUT
      - DELETE
      - OPTIONS
    allowed_headers:
      - Content-Type
      - Authorization
      - X-Request-ID
    allow_credentials: true
  rate_limit:
    enabled: true
    requests_per_minute: 100
    burst: 10

9.2 监控和日志

// internal/infrastructure/monitoring/metrics.go
package monitoring

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
)

var (
    // HTTP请求指标
    httpRequestsTotal = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total number of HTTP requests",
        },
        []string{"method", "path", "status"},
    )
    
    httpRequestDuration = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request duration in seconds",
            Buckets: prometheus.DefBuckets,
        },
        []string{"method", "path", "status"},
    )
    
    // 业务指标
    userRegistrationsTotal = promauto.NewCounter(
        prometheus.CounterOpts{
            Name: "user_registrations_total",
            Help: "Total number of user registrations",
        },
    )
    
    activeUsersGauge = promauto.NewGauge(
        prometheus.GaugeOpts{
            Name: "active_users_count",
            Help: "Number of active users",
        },
    )
    
    // 数据库指标
    dbQueriesTotal = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "db_queries_total",
            Help: "Total number of database queries",
        },
        []string{"operation", "table"},
    )
    
    dbQueryDuration = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "db_query_duration_seconds",
            Help:    "Database query duration in seconds",
            Buckets: prometheus.DefBuckets,
        },
        []string{"operation", "table"},
    )
)

// RecordHTTPRequest 记录HTTP请求
func RecordHTTPRequest(method, path, status string, duration float64) {
    httpRequestsTotal.WithLabelValues(method, path, status).Inc()
    httpRequestDuration.WithLabelValues(method, path, status).Observe(duration)
}

// RecordUserRegistration 记录用户注册
func RecordUserRegistration() {
    userRegistrationsTotal.Inc()
}

// SetActiveUsers 设置活跃用户数量
func SetActiveUsers(count float64) {
    activeUsersGauge.Set(count)
}

// RecordDBQuery 记录数据库查询
func RecordDBQuery(operation, table string, duration float64) {
    dbQueriesTotal.WithLabelValues(operation, table).Inc()
    dbQueryDuration.WithLabelValues(operation, table).Observe(duration)
}

9.3 健康检查和就绪检查

// internal/interface/http/handler/health_handler.go
package httphandler

import (
    "net/http"
    "time"
    
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
    
    "ecommerce-api/internal/infrastructure/response"
)

// HealthHandler 健康检查处理器
type HealthHandler struct {
    db *gorm.DB
}

func NewHealthHandler(db *gorm.DB) *HealthHandler {
    return &HealthHandler{db: db}
}

// HealthCheck 健康检查
// @Summary Health check
// @Description Check if the service is healthy
// @Tags health
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Failure 503 {object} response.Response
// @Router /health [get]
func (h *HealthHandler) HealthCheck(c *gin.Context) {
    health := h.checkHealth()
    
    if health.Status == "healthy" {
        response.Success(c, http.StatusOK, "Service is healthy", health)
    } else {
        response.Error(c, http.StatusServiceUnavailable, "unhealthy", "Service is unhealthy", health)
    }
}

// ReadinessCheck 就绪检查
// @Summary Readiness check
// @Description Check if the service is ready to serve requests
// @Tags health
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Failure 503 {object} response.Response
// @Router /ready [get]
func (h *HealthHandler) ReadinessCheck(c *gin.Context) {
    ready := h.checkReadiness()
    
    if ready.Status == "ready" {
        response.Success(c, http.StatusOK, "Service is ready", ready)
    } else {
        response.Error(c, http.StatusServiceUnavailable, "not_ready", "Service is not ready", ready)
    }
}

// LivenessCheck 存活检查
// @Summary Liveness check
// @Description Check if the service is alive
// @Tags health
// @Accept json
// @Produce json
// @Success 200 {object} response.Response
// @Router /live [get]
func (h *HealthHandler) LivenessCheck(c *gin.Context) {
    response.Success(c, http.StatusOK, "Service is alive", gin.H{
        "status": "alive",
        "timestamp": time.Now().Unix(),
    })
}

type HealthStatus struct {
    Status    string            `json:"status"`
    Timestamp int64             `json:"timestamp"`
    Checks    map[string]Check  `json:"checks"`
}

type Check struct {
    Status  string        `json:"status"`
    Message string        `json:"message,omitempty"`
    Latency time.Duration `json:"latency,omitempty"`
}

func (h *HealthHandler) checkHealth() HealthStatus {
    checks := make(map[string]Check)
    overallStatus := "healthy"
    
    // 检查数据库连接
    start := time.Now()
    sqlDB, err := h.db.DB()
    if err != nil {
        checks["database"] = Check{
            Status:  "unhealthy",
            Message: "Failed to get database connection: " + err.Error(),
        }
        overallStatus = "unhealthy"
    } else {
        if err := sqlDB.Ping(); err != nil {
            checks["database"] = Check{
                Status:  "unhealthy",
                Message: "Database ping failed: " + err.Error(),
            }
            overallStatus = "unhealthy"
        } else {
            checks["database"] = Check{
                Status:  "healthy",
                Latency: time.Since(start),
            }
        }
    }
    
    return HealthStatus{
        Status:    overallStatus,
        Timestamp: time.Now().Unix(),
        Checks:    checks,
    }
}

func (h *HealthHandler) checkReadiness() HealthStatus {
    checks := make(map[string]Check)
    overallStatus := "ready"
    
    // 检查数据库是否就绪
    sqlDB, err := h.db.DB()
    if err != nil {
        checks["database"] = Check{
            Status:  "not_ready",
            Message: "Database not available",
        }
        overallStatus = "not_ready"
    } else {
        if err := sqlDB.Ping(); err != nil {
            checks["database"] = Check{
                Status:  "not_ready",
                Message: "Database not responding",
            }
            overallStatus = "not_ready"
        } else {
            checks["database"] = Check{
                Status: "ready",
            }
        }
    }
    
    return HealthStatus{
        Status:    overallStatus,
        Timestamp: time.Now().Unix(),
        Checks:    checks,
    }
}

总结

本教程完整实现了一个生产级的RESTful后端服务,涵盖了:

架构设计

  • 分层架构:清晰的领域层、应用层、基础设施层分离
  • DDD实现:聚合根、实体、值对象、仓储模式
  • 依赖注入:松耦合的组件设计

核心功能

  • CRUD操作:完整的用户管理功能
  • 认证授权:JWT-based认证机制
  • 数据验证:输入验证和业务规则验证
  • 错误处理:统一的错误响应格式

测试策略

  • 单元测试:使用mock对象隔离测试
  • 集成测试:端到端的功能测试
  • Table-Driven测试:覆盖多种场景
  • 测试覆盖率:全面的测试覆盖

容器化部署

  • Docker化:多阶段构建优化镜像大小
  • Docker Compose:开发环境一键启动
  • 生产配置:性能优化和安全配置
  • 健康检查:容器健康状态监控

运维监控

  • 日志系统:结构化日志记录
  • 性能监控:Prometheus指标收集
  • 配置管理:环境分离的配置管理
  • 构建自动化:Makefile简化开发流程

最佳实践

  • 代码组织:清晰的项目结构
  • 错误处理:一致的错误处理机制
  • 安全考虑:输入验证和SQL注入防护
  • 性能优化:连接池和缓存策略

这个架构提供了:

  • 可维护性:清晰的代码结构和文档
  • 可测试性:易于测试的设计
  • 可扩展性:支持业务增长和功能扩展
  • 可部署性:容器化支持各种部署环境

通过掌握这些技术,你可以构建企业级的Go应用程序,满足生产环境的要求。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐