在电商系统的开发过程中,购物车是用户下单前暂存商品的核心模块,使用Golang实现购物车功能需要兼顾逻辑合理性和数据存储的高效性。本文将以session存储购物车数据为例,演示商品添加与删除的完整实现过程。

购物车功能的核心需求
购物车的基础功能包含商品添加、商品删除、数量修改、清空购物车等,其中添加和删除是最核心的两个操作。我们需要先明确这两个操作的基本逻辑:
- 商品添加:判断购物车中是否已有该商品,已有则增加数量,没有则新增商品记录
- 商品删除:根据商品ID从购物车记录中移除对应商品,支持单个商品删除和批量删除
数据结构定义
首先我们需要定义购物车和购物车商品的基础结构体,方便后续的逻辑处理和数据存储:
// 购物车商品结构体
type CartItem struct {
ProductID int64 // 商品ID
Name string // 商品名称
Price float64 // 商品单价
Quantity int // 商品数量
}
// 购物车结构体
type Cart struct {
Items map[int64]*CartItem // 商品ID到购物车商品的映射,方便快速查找
}
初始化购物车
使用session存储购物车时,每次用户访问需要先获取或初始化购物车实例:
package cart
import (
"net/http"
"github.com/gorilla/sessions"
)
var store = sessions.NewCookieStore([]byte("cart-secret-key"))
// 获取或初始化购物车
func GetCart(r *http.Request) (*Cart, error) {
session, err := store.Get(r, "cart-session")
if err != nil {
return nil, err
}
// 从session中获取购物车数据
cartData, ok := session.Values["cart"]
if !ok {
// 初始化空购物车
return &Cart{
Items: make(map[int64]*CartItem),
}, nil
}
// 类型断言转换购物车数据
cart, ok := cartData.(*Cart)
if !ok {
return &Cart{
Items: make(map[int64]*CartItem),
}, nil
}
return cart, nil
}
// 保存购物车到session
func SaveCart(w http.ResponseWriter, r *http.Request, cart *Cart) error {
session, err := store.Get(r, "cart-session")
if err != nil {
return err
}
session.Values["cart"] = cart
return session.Save(r, w)
}
商品添加功能实现
商品添加需要先获取购物车实例,然后判断商品是否已存在,再执行对应的更新逻辑:
// 添加商品到购物车
func AddItemToCart(w http.ResponseWriter, r *http.Request, productID int64, name string, price float64, quantity int) error {
// 获取当前购物车
cart, err := GetCart(r)
if err != nil {
return err
}
// 判断商品是否已在购物车中
if item, exists := cart.Items[productID]; exists {
// 已存在,增加数量
item.Quantity += quantity
} else {
// 不存在,新增商品记录
cart.Items[productID] = &CartItem{
ProductID: productID,
Name: name,
Price: price,
Quantity: quantity,
}
}
// 保存购物车到session
return SaveCart(w, r, cart)
}
商品删除功能实现
商品删除支持两种场景,一种是删除单个商品,另一种是清空整个购物车:
// 从购物车删除单个商品
func RemoveItemFromCart(w http.ResponseWriter, r *http.Request, productID int64) error {
cart, err := GetCart(r)
if err != nil {
return err
}
// 删除对应商品ID的记录
delete(cart.Items, productID)
return SaveCart(w, r, cart)
}
// 清空购物车
func ClearCart(w http.ResponseWriter, r *http.Request) error {
cart, err := GetCart(r)
if err != nil {
return err
}
// 清空商品映射
cart.Items = make(map[int64]*CartItem)
return SaveCart(w, r, cart)
}
接口层调用示例
最后我们可以编写HTTP接口来暴露购物车的添加和删除能力,供前端调用:
package main
import (
"encoding/json"
"net/http"
"strconv"
"cart"
)
// 添加商品接口
func addItemHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "仅支持POST请求", http.StatusMethodNotAllowed)
return
}
// 解析请求参数
productID, _ := strconv.ParseInt(r.FormValue("product_id"), 10, 64)
name := r.FormValue("name")
price, _ := strconv.ParseFloat(r.FormValue("price"), 64)
quantity, _ := strconv.Atoi(r.FormValue("quantity"))
err := cart.AddItemToCart(w, r, productID, name, price, quantity)
if err != nil {
http.Error(w, "添加商品失败", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"msg": "添加成功"})
}
// 删除商品接口
func removeItemHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "仅支持POST请求", http.StatusMethodNotAllowed)
return
}
productID, _ := strconv.ParseInt(r.FormValue("product_id"), 10, 64)
err := cart.RemoveItemFromCart(w, r, productID)
if err != nil {
http.Error(w, "删除商品失败", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{"msg": "删除成功"})
}
func main() {
http.HandleFunc("/cart/add", addItemHandler)
http.HandleFunc("/cart/remove", removeItemHandler)
http.ListenAndServe(":8080", nil)
}
注意事项
上述示例使用session存储购物车数据,适合小型应用或者用户未登录的场景,如果是大型电商系统,建议将购物车数据存储到Redis中,既可以支持用户登录后跨设备同步购物车数据,也能减轻服务端的存储压力。同时需要注意对商品数量的合法性校验,避免出现数量为负数的情况。