15分钟学 Go 实战项目五 :简单电子商务网站(3W字完整例子)

简单的电子商务网站开发实战

项目概述

目标

  • 实现用户注册登录功能
  • 开发商品浏览和搜索功能
  • 实现购物车管理
  • 完成订单处理流程

技术栈

类别技术选择说明
Web框架Gin高性能HTTP框架
数据库MySQL存储用户和商品信息
缓存Redis购物车和会话管理
ORMGORM数据库操作
认证JWT用户身份验证

一、项目结构

在这里插入图片描述

二、核心代码实现

1. 数据库模型定义

package model

import (
    "time"
    "gorm.io/gorm"
)

// User 用户模型
type User struct {
    gorm.Model
    Username string `gorm:"unique;not null" json:"username"`
    Password string `gorm:"not null" json:"-"`
    Email    string `gorm:"unique;not null" json:"email"`
    Role     string `gorm:"default:'user'" json:"role"`
}

// Product 商品模型
type Product struct {
    gorm.Model
    Name        string  `gorm:"not null" json:"name"`
    Description string  `json:"description"`
    Price       float64 `gorm:"not null" json:"price"`
    Stock       int     `gorm:"not null" json:"stock"`
    CategoryID  uint    `json:"category_id"`
}

// CartItem 购物车项目
type CartItem struct {
    gorm.Model
    UserID    uint    `gorm:"not null" json:"user_id"`
    ProductID uint    `gorm:"not null" json:"product_id"`
    Quantity  int     `gorm:"not null" json:"quantity"`
    Product   Product `gorm:"foreignKey:ProductID" json:"product"`
}

// Order 订单
type Order struct {
    gorm.Model
    UserID      uint       `gorm:"not null" json:"user_id"`
    TotalAmount float64    `gorm:"not null" json:"total_amount"`
    Status      string     `gorm:"default:'pending'" json:"status"`
    Items       []OrderItem `gorm:"foreignKey:OrderID" json:"items"`
}

// OrderItem 订单项目
type OrderItem struct {
    gorm.Model
    OrderID   uint    `gorm:"not null" json:"order_id"`
    ProductID uint    `gorm:"not null" json:"product_id"`
    Quantity  int     `gorm:"not null" json:"quantity"`
    Price     float64 `gorm:"not null" json:"price"`
    Product   Product `gorm:"foreignKey:ProductID" json:"product"`
}

2. 用户认证中间件

package middleware

import (
    "github.com/gin-gonic/gin"
    "github.com/dgrijalva/jwt-go"
    "net/http"
    "strings"
    "time"
)

const (
    secretKey = "your-secret-key"
)

// Claims JWT claims结构
type Claims struct {
    UserID uint   `json:"user_id"`
    Role   string `json:"role"`
    jwt.StandardClaims
}

// GenerateToken 生成JWT token
func GenerateToken(userID uint, role string) (string, error) {
    claims := Claims{
        UserID: userID,
        Role:   role,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
            IssuedAt:  time.Now().Unix(),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString([]byte(secretKey))
}

// AuthMiddleware 认证中间件
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
            c.Abort()
            return
        }

        tokenString := strings.Replace(authHeader, "Bearer ", "", 1)
        claims := &Claims{}

        token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
            return []byte(secretKey), nil
        })

        if err != nil || !token.Valid {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
            c.Abort()
            return
        }

        // 将用户信息存储到上下文
        c.Set("userID", claims.UserID)
        c.Set("role", claims.Role)
        c.Next()
    }
}

// AdminRequired 管理员权限中间件
func AdminRequired() gin.HandlerFunc {
    return func(c *gin.Context) {
        role, exists := c.Get("role")
        if !exists || role != "admin" {
            c.JSON(http.StatusForbidden, gin.H{"error": "Admin privileges required"})
            c.Abort()
            return
        }
        c.Next()
    }
}

3. 购物车服务实现

package service

import (
    "errors"
    "github.com/your/project/internal/model"
    "gorm.io/gorm"
)

type CartService struct {
    db *gorm.DB
}

func NewCartService(db *gorm.DB) *CartService {
    return &CartService{db: db}
}

// AddToCart 添加商品到购物车
func (s *CartService) AddToCart(userID uint, productID uint, quantity int) error {
    // 检查商品是否存在
    var product model.Product
    if err := s.db.First(&product, productID).Error; err != nil {
        return errors.New("product not found")
    }

    // 检查库存
    if product.Stock < quantity {
        return errors.New("insufficient stock")
    }

    // 查找购物车中是否已存在该商品
    var cartItem model.CartItem
    result := s.db.Where("user_id = ? AND product_id = ?", userID, productID).First(&cartItem)

    if result.Error == nil {
        // 更新数量
        cartItem.Quantity += quantity
        return s.db.Save(&cartItem).Error
    }

    // 创建新的购物车项
    cartItem = model.CartItem{
        UserID:    userID,
        ProductID: productID,
        Quantity:  quantity,
    }
    return s.db.Create(&cartItem).Error
}

// GetCart 获取购物车内容
func (s *CartService) GetCart(userID uint) ([]model.CartItem, error) {
    var cartItems []model.CartItem
    err := s.db.Preload("Product").Where("user_id = ?", userID).Find(&cartItems).Error
    return cartItems, err
}

// UpdateCartItem 更新购物车项目数量
func (s *CartService) UpdateCartItem(userID uint, itemID uint, quantity int) error {
    var cartItem model.CartItem
    if err := s.db.Where("id = ? AND user_id = ?", itemID, userID).First(&cartItem).Error; err != nil {
        return errors.New("cart item not found")
    }

    // 检查库存
    var product model.Product
    if err := s.db.First(&product, cartItem.ProductID).Error; err != nil {
        return errors.New("product not found")
    }

    if product.Stock < quantity {
        return errors.New("insufficient stock")
    }

    cartItem.Quantity = quantity
    return s.db.Save(&cartItem).Error
}

// RemoveFromCart 从购物车移除商品
func (s *CartService) RemoveFromCart(userID uint, itemID uint) error {
    result := s.db.Where("id = ? AND user_id = ?", itemID, userID).Delete(&model.CartItem{})
    if result.RowsAffected == 0 {
        return errors.New("cart item not found")
    }
    return result.Error
}

// ClearCart 清空购物车
func (s *CartService) ClearCart(userID uint) error {
    return s.db.Where("user_id = ?", userID).Delete(&model.CartItem{}).Error
}

// GetCartTotal 计算购物车总金额
func (s *CartService) GetCartTotal(userID uint) (float64, error) {
    var total float64
    var cartItems []model.CartItem
    err := s.db.Preload("Product").Where("user_id = ?", userID).Find(&cartItems).Error
    if err != nil {
        return 0, err
    }

    for _, item := range cartItems {
        total += item.Product.Price * float64(item.Quantity)
    }
    return total, nil
}

4. API路由定义

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/your/project/internal/handler"
    "github.com/your/project/internal/middleware"
)

func setupRouter(h *handler.Handler) *gin.Engine {
    r := gin.Default()

    // 公开路由
    public := r.Group("/api/v1")
    {
        // 用户认证
        public.POST("/register", h.Register)
        public.POST("/login", h.Login)
        
        // 商品
        public.GET("/products", h.ListProducts)
        public.GET("/products/:id", h.GetProduct)
        public.GET("/products/search", h.SearchProducts)
    }

    // 需要认证的路由
    authorized := r.Group("/api/v1")
    authorized.Use(middleware.AuthMiddleware())
    {
        // 用户相关
        authorized.GET("/profile", h.GetProfile)
        authorized.PUT("/profile", h.UpdateProfile)
        
        // 购物车
        authorized.GET("/cart", h.GetCart)
        authorized.POST("/cart", h.AddToCart)
        authorized.PUT("/cart/:id", h.UpdateCartItem)
        authorized.DELETE("/cart/:id", h.RemoveFromCart)
        authorized.DELETE("/cart", h.ClearCart)
        
        // 订单
        authorized.POST("/orders", h.CreateOrder)
        authorized.GET("/orders", h.ListOrders)
        authorized.GET("/orders/:id", h.GetOrder)
    }

    // 管理员路由
    admin := r.Group("/api/v1/admin")
    admin.Use(middleware.AuthMiddleware(), middleware.AdminRequired())
    {
        // 商品管理
        admin.POST("/products", h.CreateProduct)
        admin.PUT("/products/:id", h.UpdateProduct)
        admin.DELETE("/products/:id", h.DeleteProduct)
        
        // 订单管理
        admin.GET("/orders", h.ListAllOrders)
        admin.PUT("/orders/:id/status", h.UpdateOrderStatus)
    }

    return r
}

三、流程图示

1. 用户注册登录流程

在这里插入图片描述

2. 购物车操作流程

在这里插入图片描述

四、处理器实现

1. 用户处理器

package handler

import (
    "github.com/gin-gonic/gin"
    "github.com/your/project/internal/model"
    "github.com/your/project/internal/service"
    "net/http"
)

type UserHandler struct {
    userService *service.UserService
}

func NewUserHandler(userService *service.UserService) *UserHandler {
    return &UserHandler{userService: userService}
}

// Register 用户注册
func (h *UserHandler) Register(c *gin.Context) {
    var input struct {
        Username string `json:"username" binding:"required,min=3,max=32"`
        Password string `json:"password" binding:"required,min=6"`
        Email    string `json:"email" binding:"required,email"`
    }

    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user, err := h.userService.CreateUser(input.Username, input.Password, input.Email)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    token, err := service.GenerateToken(user.ID, user.Role)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Error generating token"})
        return
    }

    c.JSON(http.StatusCreated, gin.H{
        "token": token,
        "user":  user,
    })
}

// Login 用户登录
func (h *UserHandler) Login(c *gin.Context) {
    var input struct {
        Username string `json:"username" binding:"required"`
        Password string `json:"password" binding:"required"`
    }

    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user, err := h.userService.ValidateUser(input.Username, input.Password)
    if err != nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
        return
    }

    token, err := service.GenerateToken(user.ID, user.Role)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Error generating token"})
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "token": token,
        "user":  user,
    })
}

// GetProfile 获取用户信息
func (h *UserHandler) GetProfile(c *gin.Context) {
    userID, _ := c.Get("userID")
    user, err := h.userService.GetUserByID(userID.(uint))
    if err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
        return
    }

    c.JSON(http.StatusOK, gin.H{"user": user})
}

// UpdateProfile 更新用户信息
func (h *UserHandler) UpdateProfile(c *gin.Context) {
    userID, _ := c.Get("userID")
    
    var input struct {
        Email    string `json:"email" binding:"omitempty,email"`
        Password string `json:"password" binding:"omitempty,min=6"`
    }

    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    user, err := h.userService.UpdateUser(userID.(uint), input.Email, input.Password)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{"user": user})
}

2. 购物车处理器

package handler

import (
    "github.com/gin-gonic/gin"
    "github.com/your/project/internal/service"
    "net/http"
    "strconv"
)

type CartHandler struct {
    cartService *service.CartService
}

func NewCartHandler(cartService *service.CartService) *CartHandler {
    return &CartHandler{cartService: cartService}
}

// GetCart 获取购物车内容
func (h *CartHandler) GetCart(c *gin.Context) {
    userID := c.GetUint("userID")
    
    cartItems, err := h.cartService.GetCart(userID)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    total, err := h.cartService.GetCartTotal(userID)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "items": cartItems,
        "total": total,
    })
}

// AddToCart 添加商品到购物车
func (h *CartHandler) AddToCart(c *gin.Context) {
    userID := c.GetUint("userID")
    
    var input struct {
        ProductID uint `json:"product_id" binding:"required"`
        Quantity  int  `json:"quantity" binding:"required,min=1"`
    }

    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    err := h.cartService.AddToCart(userID, input.ProductID, input.Quantity)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{"message": "Item added to cart"})
}

// UpdateCartItem 更新购物车商品数量
func (h *CartHandler) UpdateCartItem(c *gin.Context) {
    userID := c.GetUint("userID")
    itemID, err := strconv.ParseUint(c.Param("id"), 10, 64)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid item ID"})
        return
    }

    var input struct {
        Quantity int `json:"quantity" binding:"required,min=1"`
    }

    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    err = h.cartService.UpdateCartItem(userID, uint(itemID), input.Quantity)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{"message": "Cart item updated"})
}

// RemoveFromCart 从购物车移除商品
func (h *CartHandler) RemoveFromCart(c *gin.Context) {
    userID := c.GetUint("userID")
    itemID, err := strconv.ParseUint(c.Param("id"), 10, 64)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid item ID"})
        return
    }

    err = h.cartService.RemoveFromCart(userID, uint(itemID))
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{"message": "Item removed from cart"})
}

// ClearCart 清空购物车
func (h *CartHandler) ClearCart(c *gin.Context) {
    userID := c.GetUint("userID")

    err := h.cartService.ClearCart(userID)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }

    c.JSON(http.StatusOK, gin.H{"message": "Cart cleared"})
}

五、API接口说明

用户相关接口

接口方法描述请求参数返回数据
/api/v1/registerPOST用户注册username, password, emailtoken, user
/api/v1/loginPOST用户登录username, passwordtoken, user
/api/v1/profileGET获取用户信息-user
/api/v1/profilePUT更新用户信息email, passworduser

购物车相关接口

接口方法描述请求参数返回数据
/api/v1/cartGET获取购物车-items, total
/api/v1/cartPOST添加商品product_id, quantitymessage
/api/v1/cart/:idPUT更新商品数量quantitymessage
/api/v1/cart/:idDELETE删除商品-message
/api/v1/cartDELETE清空购物车-message

六、测试案例

1. 用户服务测试

package service_test

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/your/project/internal/service"
    "github.com/your/project/internal/model"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

func setupTestDB(t *testing.T) *gorm.DB {
    db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
    assert.NoError(t, err)
    
    err = db.AutoMigrate(&model.User{})
    assert.NoError(t, err)
    
    return db
}

func TestUserService_CreateUser(t *testing.T) {
    db := setupTestDB(t)
    userService := service.NewUserService(db)

    tests := []struct {
        name     string
        username string
        password string
        email    string
        wantErr  bool
    }{
        {
            name:     "Valid user creation",
            username: "testuser",
            password: "password123",
            email:    "test@example.com",
            wantErr:  false,
        },
        {
            name:     "Duplicate username",
            username: "testuser",
            password: "password123",
            email:    "test2@example.com",
            wantErr:  true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            user, err := userService.CreateUser(tt.username, tt.password, tt.email)
            if tt.wantErr {
                assert.Error(t, err)
                return
            }
            
            assert.NoError(t, err)
            assert.NotNil(t, user)
            assert.Equal(t, tt.username, user.Username)
            assert.Equal(t, tt.email, user.Email)
            assert.NotEqual(t, tt.password, user.Password) // 密码应该已加密
        })
    }
}

func TestUserService_ValidateUser(t *testing.T) {
    db := setupTestDB(t)
    userService := service.NewUserService(db)

    // 创建测试用户
    testUser, err := userService.CreateUser("testuser", "password123", "test@example.com")
    assert.NoError(t, err)

    tests := []struct {
        name     string
        username string
        password string
        wantErr  bool
    }{
        {
            name:     "Valid credentials",
            username: "testuser",
            password: "password123",
            wantErr:  false,
        },
        {
            name:     "Invalid password",
            username: "testuser",
            password: "wrongpassword",
            wantErr:  true,
        },
        {
            name:     "Non-existent user",
            username: "nonexistent",
            password: "password123",
            wantErr:  true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            user, err := userService.ValidateUser(tt.username, tt.password)
            if tt.wantErr {
                assert.Error(t, err)
                return
            }
            
            assert.NoError(t, err)
            assert.NotNil(t, user)
            assert.Equal(t, testUser.ID, user.ID)
        })
    }
}

2. 购物车服务测试

package service_test

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/your/project/internal/service"
    "github.com/your/project/internal/model"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

func setupCartTestDB(t *testing.T) *gorm.DB {
    db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
    assert.NoError(t, err)
    
    err = db.AutoMigrate(&model.User{}, &model.Product{}, &model.CartItem{})
    assert.NoError(t, err)
    
    return db
}

func createTestProduct(db *gorm.DB) *model.Product {
    product := &model.Product{
        Name:        "Test Product",
        Description: "Test Description",
        Price:       99.99,
        Stock:       100,
    }
    db.Create(product)
    return product
}

func TestCartService_AddToCart(t *testing.T) {
    db := setupCartTestDB(t)
    cartService := service.NewCartService(db)
    product := createTestProduct(db)

    tests := []struct {
        name      string
        userID    uint
        productID uint
        quantity  int
        wantErr   bool
    }{
        {
            name:      "Valid addition to cart",
            userID:    1,
            productID: product.ID,
            quantity:  5,
            wantErr:   false,
        },
        {
            name:      "Exceed stock quantity",
            userID:    1,
            productID: product.ID,
            quantity:  101,
            wantErr:   true,
        },
        {
            name:      "Non-existent product",
            userID:    1,
            productID: 999,
            quantity:  1,
            wantErr:   true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := cartService.AddToCart(tt.userID, tt.productID, tt.quantity)
            if tt.wantErr {
                assert.Error(t, err)
                return
            }
            
            assert.NoError(t, err)
            
            // 验证购物车项目
            var cartItem model.CartItem
            err = db.Where("user_id = ? AND product_id = ?", tt.userID, tt.productID).First(&cartItem).Error
            assert.NoError(t, err)
            assert.Equal(t, tt.quantity, cartItem.Quantity)
        })
    }
}

func TestCartService_GetCartTotal(t *testing.T) {
    db := setupCartTestDB(t)
    cartService := service.NewCartService(db)
    
    // 创建测试产品
    product1 := &model.Product{Name: "Product 1", Price: 10.00, Stock: 100}
    product2 := &model.Product{Name: "Product 2", Price: 20.00, Stock: 100}
    db.Create(product1)
    db.Create(product2)
    
    // 添加商品到购物车
    cartService.AddToCart(1, product1.ID, 2) // 2 * 10.00 = 20.00
    cartService.AddToCart(1, product2.ID, 1) // 1 * 20.00 = 20.00
    
    // 计算总价
    total, err := cartService.GetCartTotal(1)
    assert.NoError(t, err)
    assert.Equal(t, 40.00, total) // 20.00 + 20.00 = 40.00
}

func TestCartService_RemoveFromCart(t *testing.T) {
    db := setupCartTestDB(t)
    cartService := service.NewCartService(db)
    product := createTestProduct(db)
    
    // 先添加商品到购物车
    err := cartService.AddToCart(1, product.ID, 1)
    assert.NoError(t, err)
    
    // 获取购物车项目ID
    var cartItem model.CartItem
    err = db.Where("user_id = ? AND product_id = ?", 1, product.ID).First(&cartItem).Error
    assert.NoError(t, err)
    
    // 测试移除
    err = cartService.RemoveFromCart(1, cartItem.ID)
    assert.NoError(t, err)
    
    // 验证商品已被移除
    var count int64
    db.Model(&model.CartItem{}).Where("id = ?", cartItem.ID).Count(&count)
    assert.Equal(t, int64(0), count)
}

七、性能优化建议

1. 数据库优化

  1. 索引优化

    • 为常用查询字段添加索引
    • 用户表:username, email
    • 商品表:category_id, price
    • 订单表:user_id, status
  2. 查询优化

    • 使用预加载避免N+1问题
    • 分页加载大量数据
    • 适当使用缓存

2. 缓存策略

在这里插入图片描述

3. 购物车优化实现

package service

import (
    "context"
    "encoding/json"
    "fmt"
    "github.com/go-redis/redis/v8"
    "github.com/your/project/internal/model"
    "gorm.io/gorm"
    "time"
)

type CachedCartService struct {
    db    *gorm.DB
    redis *redis.Client
}

func NewCachedCartService(db *gorm.DB, redis *redis.Client) *CachedCartService {
    return &CachedCartService{
        db:    db,
        redis: redis,
    }
}

// 生成购物车缓存key
func cartKey(userID uint) string {
    return fmt.Sprintf("cart:user:%d", userID)
}

// AddToCart 添加商品到购物车(带缓存)
func (s *CachedCartService) AddToCart(userID uint, productID uint, quantity int) error {
    ctx := context.Background()

    // 检查商品并锁定库存
    var product model.Product
    err := s.db.Transaction(func(tx *gorm.DB) error {
        if err := tx.Lock().First(&product, productID).Error; err != nil {
            return err
        }
        
        if product.Stock < quantity {
            return fmt.Errorf("insufficient stock")
        }
        
        return nil
    })
    if err != nil {
        return err
    }

    // 更新数据库
    cartItem := model.CartItem{
        UserID:    userID,
        ProductID: productID,
        Quantity:  quantity,
        Product:   product,
    }
    
    if err := s.db.Create(&cartItem).Error; err != nil {
        return err
    }

    // 更新缓存
    cartItemJSON, err := json.Marshal(cartItem)
    if err != nil {
        return err
    }

    key := cartKey(userID)
    pipe := s.redis.Pipeline()
    pipe.HSet(ctx, key, fmt.Sprintf("item:%d", cartItem.ID), cartItemJSON)
    pipe.Expire(ctx, key, 24*time.Hour) // 设置过期时间
    _, err = pipe.Exec(ctx)

    return err
}

// GetCart 获取购物车内容(带缓存)
func (s *CachedCartService) GetCart(userID uint) ([]model.CartItem, error) {
    ctx := context.Background()
    key := cartKey(userID)

    // 尝试从缓存获取
    cartData, err := s.redis.HGetAll(ctx, key).Result()
    if err == nil && len(cartData) > 0 {
        items := make([]model.CartItem, 0, len(cartData))
        for _, itemJSON := range cartData {
            var item model.CartItem
            if err := json.Unmarshal([]byte(itemJSON), &item); err != nil {
                continue
            }
            items = append(items, item)
        }
        return items, nil
    }

    // 缓存未命中,从数据库获取
    var items []model.CartItem
    if err := s.db.Preload("Product").Where("user_id = ?", userID).Find(&items).Error; err != nil {
        return nil, err
    }

    // 更新缓存
    pipe := s.redis.Pipeline()
    for _, item := range items {
        itemJSON, err := json.Marshal(item)
        if err != nil {
            continue
        }
        pipe.HSet(ctx, key, fmt.Sprintf("item:%d", item.ID), itemJSON)
    }
    pipe.Expire(ctx, key, 24*time.Hour)
    pipe.Exec(ctx)

    return items, nil
}

// RemoveFromCart 从购物车移除商品(带缓存)
func (s *CachedCartService) RemoveFromCart(userID uint, itemID uint) error {
    ctx := context.Background()

    // 删除数据库记录
    if err := s.db.Where("id = ? AND user_id = ?", itemID, userID).Delete(&model.CartItem{}).Error; err != nil {
        return err
    }

    // 删除缓存
    key := cartKey(userID)
    s.redis.HDel(ctx, key, fmt.Sprintf("item:%d", itemID))

    return nil
}

// UpdateCartItem 更新购物车商品数量(带缓存)
func (s *CachedCartService) UpdateCartItem(userID uint, itemID uint, quantity int) error {
    ctx := context.Background()

    // 更新数据库
    var cartItem model.CartItem
    err := s.db.Transaction(func(tx *gorm.DB) error {
        if err := tx.Where("id = ? AND user_id = ?", itemID, userID).First(&cartItem).Error; err != nil {
            return err
        }

        // 检查库存
        var product model.Product
        if err := tx.First(&product, cartItem.ProductID).Error; err != nil {
            return err
        }

        if product.Stock < quantity {
            return fmt.Errorf("insufficient stock")
        }

        cartItem.Quantity = quantity
        return tx.Save(&cartItem).Error
    })
    if err != nil {
        return err
    }

    // 更新缓存
    itemJSON, err := json.Marshal(cartItem)
    if err != nil {
        return err
    }

    key := cartKey(userID)
    return s.redis.HSet(ctx, key, fmt.Sprintf("item:%d", itemID), itemJSON).Err()
}

八、错误处理和日志记录

1. 自定义错误类型

package errors

import "fmt"

// ErrorType 错误类型
type ErrorType string

const (
    ErrorTypeValidation   ErrorType = "VALIDATION_ERROR"
    ErrorTypeNotFound     ErrorType = "NOT_FOUND"
    ErrorTypeUnauthorized ErrorType = "UNAUTHORIZED"
    ErrorTypeDatabase     ErrorType = "DATABASE_ERROR"
    ErrorTypeInternal     ErrorType = "INTERNAL_ERROR"
)

// AppError 应用错误
type AppError struct {
    Type    ErrorType `json:"type"`
    Message string    `json:"message"`
    Details string    `json:"details,omitempty"`
}

func (e *AppError) Error() string {
    if e.Details != "" {
        return fmt.Sprintf("%s: %s (%s)", e.Type, e.Message, e.Details)
    }
    return fmt.Sprintf("%s: %s", e.Type, e.Message)
}

// 创建各类错误的工厂函数
func NewValidationError(message string, details string) *AppError {
    return &AppError{
        Type:    ErrorTypeValidation,
        Message: message,
        Details: details,
    }
}

func NewNotFoundError(message string) *AppError {
    return &AppError{
        Type:    ErrorTypeNotFound,
        Message: message,
    }
}

func NewUnauthorizedError(message string) *AppError {
    return &AppError{
        Type:    ErrorTypeUnauthorized,
        Message: message,
    }
}

func NewDatabaseError(err error) *AppError {
    return &AppError{
        Type:    ErrorTypeDatabase,
        Message: "Database operation failed",
        Details: err.Error(),
    }
}

func NewInternalError(err error) *AppError {
    return &AppError{
        Type:    ErrorTypeInternal,
        Message: "Internal server error",
        Details: err.Error(),
    }
}

2. 日志中间件

package middleware

import (
    "bytes"
    "github.com/gin-gonic/gin"
    "github.com/sirupsen/logrus"
    "io/ioutil"
    "time"
)

// responseWriter 自定义响应写入器
type responseWriter struct {
    gin.ResponseWriter
    body *bytes.Buffer
}

func (w responseWriter) Write(b []byte) (int, error) {
    w.body.Write(b)
    return w.ResponseWriter.Write(b)
}

// LoggingMiddleware 日志中间件
func LoggingMiddleware(logger *logrus.Logger) gin.HandlerFunc {
    return func(c *gin.Context) {
        // 开始时间
        start := time.Now()

        // 读取请求体
        var requestBody []byte
        if c.Request.Body != nil {
            requestBody, _ = ioutil.ReadAll(c.Request.Body)
            c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(requestBody))
        }

        // 包装响应写入器
        w := &responseWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
        c.Writer = w

        // 处理请求
        c.Next()

        // 请求处理时间
        duration := time.Since(start)

        // 获取用户ID(如果有)
        userID, exists := c.Get("userID")
        if !exists {
            userID = "anonymous"
        }

        // 构建日志字段
        fields := logrus.Fields{
            "client_ip":  c.ClientIP(),
            "duration":   duration,
            "method":     c.Request.Method,
            "path":       c.Request.URL.Path,
            "status":     c.Writer.Status(),
            "user_id":    userID,
            "referrer":   c.Request.Referer(),
            "user_agent": c.Request.UserAgent(),
        }

        // 添加请求体(如果不是太大)
        if len(requestBody) > 0 && len(requestBody) < 1024 {
            fields["request_body"] = string(requestBody)
        }

        // 添加响应体(如果不是太大)
        responseBody := w.body.String()
        if len(responseBody) > 0 && len(responseBody) < 1024 {
            fields["response_body"] = responseBody
        }

        // 根据状态码选择日志级别
        if c.Writer.Status() >= 500 {
            logger.WithFields(fields).Error("Server error")
        } else if c.Writer.Status() >= 400 {
            logger.WithFields(fields).Warn("Client error")
        } else {
            logger.WithFields(fields).Info("Request processed")
        }
    }
}

九、项目部署和监控

1. Docker部署配置

# Dockerfile
FROM golang:1.21-alpine AS builder

WORKDIR /app

# 安装依赖
COPY go.mod go.sum ./
RUN go mod download

# 复制源代码
COPY . .

# 编译
RUN CGO_ENABLED=0 GOOS=linux go build -o main cmd/server/main.go

# 最终镜像
FROM alpine:latest

WORKDIR /app

# 从builder阶段复制编译后的程序
COPY --from=builder /app/main .
COPY --from=builder /app/configs ./configs

# 暴露端口
EXPOSE 8080

# 启动程序
CMD ["./main"]

# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=mysql
      - DB_PORT=3306
      - DB_USER=root
      - DB_PASSWORD=secret
      - DB_NAME=ecommerce
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - JWT_SECRET=your-secret-key
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: ecommerce
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - "3306:3306"

  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data
    ports:
      - "6379:6379"

volumes:
  mysql-data:
  redis-data:

2. 性能监控实现

package middleware

import (
    "github.com/gin-gonic/gin"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
    "time"
)

var (
    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: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
        },
        []string{"method", "path"},
    )

    activeRequests = promauto.NewGauge(
        prometheus.GaugeOpts{
            Name: "http_requests_active",
            Help: "Number of active HTTP requests",
        },
    )

    responseSize = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_response_size_bytes",
            Help:    "Size of HTTP responses in bytes",
            Buckets: []float64{100, 1000, 10000, 100000, 1000000},
        },
        []string{"method", "path"},
    )
)

// MetricsMiddleware 性能监控中间件
func MetricsMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        
        // 增加活跃请求计数
        activeRequests.Inc()
        
        // 请求处理完成后执行
        defer func() {
            // 减少活跃请求计数
            activeRequests.Dec()
            
            // 记录请求总数
            status := c.Writer.Status()
            httpRequestsTotal.WithLabelValues(
                c.Request.Method,
                c.FullPath(),
                string(rune(status)),
            ).Inc()
            
            // 记录请求处理时间
            duration := time.Since(start).Seconds()
            httpRequestDuration.WithLabelValues(
                c.Request.Method,
                c.FullPath(),
            ).Observe(duration)
            
            // 记录响应大小
            responseSize.WithLabelValues(
                c.Request.Method,
                c.FullPath(),
            ).Observe(float64(c.Writer.Size()))
        }()
        
        c.Next()
    }
}

// PerformanceMonitor 系统性能监控
type PerformanceMonitor struct {
    requestCount    int64
    errorCount     int64
    responseTimes  []float64
    slowThreshold  float64 // 慢请求阈值(秒)
}

func NewPerformanceMonitor() *PerformanceMonitor {
    return &PerformanceMonitor{
        slowThreshold: 1.0, // 默认1秒为慢请求
        responseTimes: make([]float64, 0),
    }
}

// RecordMetrics 记录性能指标
func (pm *PerformanceMonitor) RecordMetrics(c *gin.Context, start time.Time) {
    duration := time.Since(start).Seconds()
    
    // 记录响应时间
    pm.responseTimes = append(pm.responseTimes, duration)
    
    // 记录请求计数
    pm.requestCount++
    
    // 记录错误计数
    if c.Writer.Status() >= 400 {
        pm.errorCount++
    }
    
    // 记录慢请求
    if duration > pm.slowThreshold {
        logrus.WithFields(logrus.Fields{
            "path":     c.Request.URL.Path,
            "method":   c.Request.Method,
            "duration": duration,
        }).Warn("Slow request detected")
    }
}

// GetMetrics 获取性能指标统计
func (pm *PerformanceMonitor) GetMetrics() map[string]interface{} {
    var avgResponseTime float64
    if len(pm.responseTimes) > 0 {
        total := 0.0
        for _, t := range pm.responseTimes {
            total += t
        }
        avgResponseTime = total / float64(len(pm.responseTimes))
    }

    return map[string]interface{}{
        "total_requests":      pm.requestCount,
        "error_rate":         float64(pm.errorCount) / float64(pm.requestCount),
        "avg_response_time":  avgResponseTime,
    }
}

3. 健康检查接口

package handler

import (
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
    "github.com/go-redis/redis/v8"
    "net/http"
    "context"
    "time"
)

type HealthHandler struct {
    db    *gorm.DB
    redis *redis.Client
}

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

// 健康检查响应结构
type HealthCheck struct {
    Status      string            `json:"status"`
    Version     string            `json:"version"`
    Services    map[string]string `json:"services"`
    Timestamp   time.Time         `json:"timestamp"`
    Environment string            `json:"environment"`
}

// Check 健康检查处理函数
func (h *HealthHandler) Check(c *gin.Context) {
    ctx := context.Background()
    services := make(map[string]string)

    // 检查数据库连接
    sqlDB, err := h.db.DB()
    if err != nil {
        services["database"] = "error: " + err.Error()
    } else if err := sqlDB.Ping(); err != nil {
        services["database"] = "error: " + err.Error()
    } else {
        services["database"] = "healthy"
    }

    // 检查Redis连接
    if err := h.redis.Ping(ctx).Err(); err != nil {
        services["redis"] = "error: " + err.Error()
    } else {
        services["redis"] = "healthy"
    }

    // 确定整体状态
    status := "healthy"
    for _, s := range services {
        if s != "healthy" {
            status = "degraded"
            break
        }
    }

    response := HealthCheck{
        Status:      status,
        Version:     "1.0.0", // 从配置中获取
        Services:    services,
        Timestamp:   time.Now(),
        Environment: "production", // 从环境变量获取
    }

    c.JSON(http.StatusOK, response)
}

// DetailedHealth 详细的健康检查
func (h *HealthHandler) DetailedHealth(c *gin.Context) {
    ctx := context.Background()
    details := make(map[string]interface{})

    // 数据库详细信息
    if sqlDB, err := h.db.DB(); err == nil {
        dbStats := make(map[string]interface{})
        dbStats["max_open_connections"] = sqlDB.Stats().MaxOpenConnections
        dbStats["open_connections"] = sqlDB.Stats().OpenConnections
        dbStats["in_use"] = sqlDB.Stats().InUse
        dbStats["idle"] = sqlDB.Stats().Idle
        details["database"] = dbStats
    }

    // Redis详细信息
    if info, err := h.redis.Info(ctx).Result(); err == nil {
        details["redis"] = map[string]interface{}{
            "info": info,
        }
    }

    // 系统信息
    details["system"] = map[string]interface{}{
        "goroutines": runtime.NumGoroutine(),
        "cgocalls":   runtime.NumCgoCall(),
        "cpus":       runtime.NumCPU(),
    }

    c.JSON(http.StatusOK, gin.H{
        "status":   "healthy",
        "details":  details,
        "timestamp": time.Now(),
    })
}

// MetricsHandler 指标收集处理器
func (h *HealthHandler) MetricsHandler(c *gin.Context) {
    metrics := make(map[string]interface{})
    
    // 收集数据库指标
    if sqlDB, err := h.db.DB(); err == nil {
        stats := sqlDB.Stats()
        metrics["database"] = map[string]interface{}{
            "connections": {
                "max_open": stats.MaxOpenConnections,
                "open":     stats.OpenConnections,
                "in_use":   stats.InUse,
                "idle":     stats.Idle,
            },
            "queries": {
                "wait_count":         stats.WaitCount,
                "wait_duration":      stats.WaitDuration,
                "max_idle_closed":    stats.MaxIdleClosed,
                "max_lifetime_closed": stats.MaxLifetimeClosed,
            },
        }
    }

    // 收集系统指标
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    metrics["system"] = map[string]interface{}{
        "memory": {
            "alloc":      m.Alloc,
            "total_alloc": m.TotalAlloc,
            "sys":        m.Sys,
            "num_gc":     m.NumGC,
        },
        "goroutines": runtime.NumGoroutine(),
    }

    c.JSON(http.StatusOK, metrics)
}

十、项目总结与最佳实践

1. 项目架构总览

在这里插入图片描述

2. 性能优化总结

优化点实现方式效果
数据库查询索引优化、连接池提高查询速度
缓存策略Redis缓存热点数据减少数据库压力
并发处理Go协程池提高并发性能
API响应GZIP压缩、分页优化响应时间
代码结构模块化、依赖注入提高可维护性

3. 安全措施

  1. 认证和授权

    • JWT Token验证
    • 角色权限控制
    • 请求频率限制
  2. 数据安全

    • 密码加密存储
    • SQL注入防护
    • XSS防护
  3. 传输安全

    • HTTPS
    • 数据加密
    • 安全Headers

4. 开发建议

  1. 代码规范

    - 使用 gofmt 格式化代码
    - 遵循 Go 官方代码规范
    - 编写完整的单元测试
    - 使用 golangci-lint 进行代码检查
    
  2. 错误处理

    - 自定义错误类型
    - 统一错误处理中间件
    - 详细的错误日志记录
    
  3. 文档维护

    - API文档使用Swagger
    - 代码注释完整
    - README文档及时更新
    

5. 部署注意事项

  1. 环境配置

    • 使用环境变量
    • 配置文件分环境
    • 敏感信息加密
  2. 监控告警

    • 设置合理的监控指标
    • 配置告警阈值
    • 建立告警机制
  3. 备份策略

    • 定期数据备份
    • 多副本存储
    • 灾难恢复预案

通过以上内容,我们完成了一个功能完整、性能优化、安全可靠的电子商务网站后端系统。项目涵盖了用户管理、商品管理、购物车功能、订单处理等核心功能,并实现了必要的性能优化和安全措施。在实际开发中,可以根据具体需求进行适当的调整和扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/917259.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++- 基于多设计模式下的同步异步日志系统

第一个项目:13万字,带源代码和详细步骤 目录 第一个项目:13万字,带源代码和详细步骤 1. 项目介绍 2. 核心技术 3. 日志系统介绍 3.1 为什么需要⽇志系统 3.2 ⽇志系统技术实现 3.2.1 同步写⽇志 3.2.2 异步写⽇志 4.知识点和单词补充 4.1单词补充 4.2知识点补充…

【AlphaFold3】开源本地的安装及使用

文章目录 安装安装DockerInstalling Docker on Host启用Rootless Docker 安装 GPU 支持安装 NVIDIA 驱动程序安装 NVIDIA 对 Docker 的支持 获取 AlphaFold 3 源代码获取基因数据库获取模型参数构建将运行 AlphaFold 3 的 Docker 容器 参考 AlphaFold3: https://github.com/goo…

【提高篇】3.4 GPIO(四,工作模式详解 下)

五,模拟输入输出 5.1 模拟功能 上下拉电阻断开,施密特触发器关闭,双 MOS 管也关闭。该模式用于 ADC 采集或者 DAC 输出,或者低功耗下省电。但要注意的是 GPIO本身并不具备模拟输出输入的功能。 5.2 模拟功能的特点 上拉电阻关闭下拉电阻关闭施密特触发器关闭双MOS管不…

向潜在安全信息和事件管理 SIEM 提供商提出的六个问题

收集和解读数据洞察以制定可用的解决方案是强大网络安全策略的基础。然而&#xff0c;组织正淹没在数据中&#xff0c;这使得这项任务变得复杂。 传统的安全信息和事件管理 ( SIEM ) 工具是组织尝试使用的一种方法&#xff0c;但由于成本、资源和可扩展性等几个原因&#xff0…

领域驱动系列-浅析VO、DTO、DO、PO

一、概念介绍 POJO&#xff08;plain ordinary java object&#xff09; &#xff1a;简单java对象&#xff0c;个人感觉POJO是最常见最多变的对象&#xff0c;是一个中间对象&#xff0c;也是我们最常打交道的对象。一个POJO持久化以后就是PO&#xff0c;直接用它传递、传递过…

网站部署到IIS后,数据库登录失败

1.数据库-安全性-登录名-NT AUTHORITY\SYSTEM-属性 2.选择用户映射选项---在里面将我们要访问的数据库选中 3.先别点确定---再选择我们刚才选择的哪个数据库,在下面的数据库角色成员身份里要选择db_owner权限

paddle表格识别数据制作

数据格式 其中主要数据有两个一个表格结构的检测框&#xff0c;一个是tokens&#xff0c;注意的地方是 1、只能使用双引号&#xff0c;单引号不行 2、使用带引号的地方是tokens里面 "<tr>", "<td", " colspan2", ">",&quo…

FPGA 第6讲 简单组合逻辑多路选择器

时间&#xff1a;2024.11.11-11.14 一、学习内容 1.组合逻辑 组合逻辑是VerilgHDL设计中一个重要组成部分。从电路本质上讲&#xff0c;组合逻辑电路的特点是输出信号只是当前时刻输入信号的函数&#xff0c;与其他时刻的输入状态无关&#xff0c;无存储电路&#xff0c;也没…

Elasticsearch 8.16.0:革新大数据搜索的新利器

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

Mysql-DQL条件查询

文章目录 条件查询比较运算符逻辑运算符范围like 关键字排序单列顺序组合排序 聚合函数分组基本的分组流程参数的区别 limit 语句limit 语法格式limit 的使用场景 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Mysql专栏&#xff1a;点击&#xff01; ⏰…

华为云租户网络-用的是隧道技术

1.验证租户网络是vxlan 2.验证用OVS 2.1控制节点VXLAN 本端ip&#xff08;local ip&#xff09;192.168.31.8 2.2计算节点VXLAN 本端ip&#xff08;local ip&#xff09;192.168.31.11 计算节点用的是bond0做隧道网络 2.3查看bond文件是否主备模式

go 集成swagger 在线接口文档

安装swaggo go install github.com/swaggo/swag/cmd/swaglatest 编写swag import ("github.com/gin-gonic/gin""goWeb/internal/service""goWeb/model/response" )// UserRouter 路由 func UserRouter(ctx *gin.RouterGroup) {ctx.GET("/…

《Python网络安全项目实战》项目6 编写密码工具程序

《Python网络安全项目实战》项目6 编写密码工具程序 项目6 编写密码工具程序任务6.1 猜数字游戏任务描述任务分析任务实施6.1.1 编写基本的猜数字程序6.1.3 测试并修改程序6.1.4 给程序增加注释 任务拓展任务实施6.2.1 生成随机密码6.2.4 菜单功能 相关知识1. 密码字典2. 密码字…

IQ Offset之工厂实例分析

有个产品 其方块图如下: FEM全名为Front End Module 详情可参照这篇 [1] WIFI前端模块的解析 这边就不赘述 而在工厂大量生产时 有一块板子 其Chain1的EVM Fail 分析Log后 发现其IQ Offset的值 比Chain2/Chain3/Chain4 还要来得差 请问 问题是出在收发器? 还是…

音视频入门基础:MPEG2-TS专题(4)——使用工具分析MPEG2-TS传输流

一、引言 有很多工具可以分析MPEG2-TS文件/流&#xff0c;比如Elecard Stream Analyzer、PROMAX TS Analyser、easyice等。下面一一对它们进行简介&#xff08;个人感觉easyice功能更强大一点&#xff09;。 二、Elecard Stream Analyzer 使用Elecard Stream Analyzer工具可以…

任务调度工具Spring Test

Spring Task 是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 作用&#xff1a;定时自动执行某段Java代码 应用场景&#xff1a; 信用卡每月还款提醒 银行贷款每月还款提醒 火车票售票系统处理未支付订单 入职纪念日为用户发送通知 一.…

ORB-SLAM2 ---- Tracking::TrackWithMotionModel()

文章目录 一、函数作用二、函数讲解三、函数代码四、调用的函数1. Tracking::UpdateLastFrame()1&#xff09;. 函数讲解2&#xff09;. 函数代码 2. ORBmatcher::SearchByProjection()1&#xff09;. 函数讲解2&#xff09;. 函数代码 3. Optimizer::PoseOptimization(Frame *…

10月月报 | Apache DolphinScheduler进展总结

各位热爱 Apache DolphinScheduler 的小伙伴们&#xff0c;社区10月份月报更新啦&#xff01;这里将记录 DolphinScheduler 社区每月的重要更新&#xff0c;欢迎关注&#xff01; 月度Merge之星 感谢以下小伙伴10月份为 Apache DolphinScheduler 所做的精彩贡献&#xff08;排…

第5章-总体设计 5.3 硬件架构设计

5.3 硬件架构设计 1.哪些类型的产品需要架构设计&#xff1f;2.硬件架构师到底做什么&#xff1f;&#xff08;1&#xff09;理解需求和业务模型的情况。&#xff08;2&#xff09;背板设计&#xff0c;既需要考虑业务数据交换能力&#xff0c;也需要考虑子模块的管理监控能力。…

深度学习工具和框架详细指南:PyTorch、TensorFlow、Keras

引言 在深度学习的世界中&#xff0c;PyTorch、TensorFlow和Keras是最受欢迎的工具和框架&#xff0c;它们为研究者和开发者提供了强大且易于使用的接口。在本文中&#xff0c;我们将深入探索这三个框架&#xff0c;涵盖如何用它们实现经典深度学习模型&#xff0c;并通过代码…