admin 管理员组

文章数量: 1087652

[golang gin框架] 27.Gin 商城项目

1.先来看一个问题

购物车数据保持到哪里?
1、购物车数据保存在本地 (cookie或者 redis缓存中),下面统一保存到cookie中,保存到redis中和cookie中逻辑步骤其实都是一样的
2、购物车数据保存到服务器(mysql) ( 必须登录)
3、没有登录,购物车数据 保存到本地 , 登录成功后购物车数据 保存到服务器 (用的最多)
增加购物车的实现逻辑:
1、获取增加购物车的数据 (把哪一个商品加入到购物车)
2、判断购物车有没有数据 (cookie或者redis缓存中是否存在当前要购买的商品)
3、如果购物车没有任何数据,直接把当前数据写入cookie
4、如果购物车有数据
(1).判断购物车有没有当前数据
有当前数据让当前数据的数量加1,然后写入到cookie
(2).如果没有当前数据直接写入cookie

2.加入购物车界面展示

以下操作都是在用户没有登录的情况下,加入购物车的操作

商品详情页面

加入购物车成功页面

购物车页面

3.代码展示

加入购物车操作说明:
1.进入商品详情页面,选择商品对应属性,点击'加入购物车'
2.加入购物车成功后,跳转到购物车成功提示页面,展示对应的商品信息
3.点击'购物车',进入到购物车页面,渲染购物车中的相关商品信息
4.在购物车页面进行相关操作
(1).全选/反选
(2).单个商品改变选中状态
(3).增加/减少商品个数
(4).删除购物车中商品

Cookie结构体

定义结构体,增加 写入数据的方法, 获取数据的方法
package models//cookie结构体import ("encoding/json""github.com/gin-gonic/gin"
)//定义结构体  缓存结构体 私有
type ginCookie struct{}//写入数据的方法
func (cookie ginCookie) Set(c *gin.Context, key string, value interface{}) {bytes, _ := json.Marshal(value)c.SetCookie(key, string(bytes), 3600, "/", "127.0.0.1", false, true)
}//获取数据的方法
func (cookie ginCookie) Get(c *gin.Context, key string, obj interface{}) bool {valueStr, err1 := c.Cookie(key)if err1 == nil && valueStr != "" && valueStr != "[]" {err2 := json.Unmarshal([]byte(valueStr), obj)return err2 == nil}return false
}//实例化结构体
var Cookie = &ginCookie{}

购物车模型代码

在models/cart.go中增加购物车结构体,里面是 购物车相关结构体属性,以及 判断购物车里面有没有当前数据的方法
package models//购物车结构体type Cart struct {Id           int  //商品idTitle        string  //商品标题Price        float64  //价格GoodsVersion string  // 版本Uid          int  //用户idNum          int  // 数量GoodsGift    string  // 赠品GoodsFitting string  // 商品配件GoodsColor   string  // 颜色GoodsImg     string  //图片GoodsAttr    string  // 其他属性Checked      bool  //商品是否选中
}//判断购物车里面有没有当前数据
func HasCartData(cartList []Cart, currentData Cart) bool {for i := 0; i < len(cartList); i++ {if cartList[i].Id == currentData.Id && cartList[i].GoodsColor == currentData.GoodsColor && cartList[i].GoodsAttr == currentData.GoodsAttr {return true}}return false
}

控制器代码

在controllers/frontend/cart.go中增加购物车相关方法:
1. 获取购物车相关列表数据
2. 添加购物车
3. 购物车增加成功跳转页面
4. 增加购物车中商品数量
5. 减少购物车中商品数量
6. 全选/反选
7. 改变一个商品数据的选中状态
8. 删除购物车
package frontendimport ("fmt""github.com/gin-gonic/gin""goshop/models""net/http"
)type CartController struct {BaseController
}//获取购物车数据
func (con CartController) Get(c *gin.Context) {//获取购物车数据 显示购物车数据cartList := []models.Cart{}models.Cookie.Get(c, "cartList", &cartList)//定义一个总价var allPrice float64for i := 0; i < len(cartList); i++ {  //计算总价: 价格*数量if cartList[i].Checked {allPrice += cartList[i].Price * float64(cartList[i].Num)}}var tpl = "frontend/cart/cart.html"con.Render(c, tpl, gin.H{"cartList": cartList,"allPrice": allPrice,})
}//添加购物车
func (con CartController) AddCart(c *gin.Context) {/*购物车数据保持到哪里?1、购物车数据保存在本地    (cookie)2、购物车数据保存到服务器(mysql)   (必须登录)3、没有登录 购物车数据保存到本地 , 登录成功后购物车数据保存到服务器  (用的最多)增加购物车的实现逻辑:1、获取增加购物车的数据  (把哪一个商品加入到购物车)2、判断购物车有没有数据   (cookie)3、如果购物车没有任何数据  直接把当前数据写入cookie4、如果购物车有数据1、判断购物车有没有当前数据有当前数据让当前数据的数量加1,然后写入到cookie2、如果没有当前数据直接写入cookie*/// 1、获取增加购物车的数据,放在结构体里面  (把一个商品加入到购物车)colorId, _ := models.Int(c.Query("color_id"))goodsId, err := models.Int(c.Query("goods_id"))if err != nil {c.Redirect(http.StatusFound, "/")}//实例化商品结构体goods := models.Goods{}//实例化商品颜色结构体goodsColor := models.GoodsColor{}//获取商品信息models.DB.Where("id = ?", goodsId).Find(&goods)//获取商品颜色信息models.DB.Where("id = ?", colorId).Find(&goodsColor)currentData := models.Cart{Id:           goodsId,Title:        goods.Title,Price:        goods.Price,GoodsVersion: goods.GoodsVersion,Num:          1,GoodsColor:   goodsColor.ColorName,GoodsImg:     goods.GoodsImg,GoodsGift:    goods.GoodsGift, /*赠品*/GoodsAttr:    "",              //根据自己的需求拓展Checked:      true,            /*默认选中*/}// 2、判断购物车有没有数据(cookie)cartList := []models.Cart{}models.Cookie.Get(c, "cartList", &cartList)fmt.Println(cartList)if len(cartList) > 0 { // 说明有数据//4、购物车有数据:判断购物车有没有当前数据if models.HasCartData(cartList, currentData) { //有当前商品数据,数量加1for i := 0; i < len(cartList); i++ {if cartList[i].Id == currentData.Id && cartList[i].GoodsColor == currentData.GoodsColor && cartList[i].GoodsAttr == currentData.GoodsAttr {cartList[i].Num = cartList[i].Num + 1}}} else { // 购物车中没有当前商品, 则加入购物车cartList = append(cartList, currentData)}//更新购物车models.Cookie.Set(c, "cartList", cartList)} else {// 3、如果购物车没有任何数据  直接把当前数据写入cookiecartList = append(cartList, currentData)fmt.Println(cartList)models.Cookie.Set(c, "cartList", cartList)}//添加购物车成功后,跳转到添加成功页面c.Redirect(http.StatusFound, "/cart/successTip?goods_id=" + models.String(goodsId))
}//购物车增加成功跳转页面
func (con CartController) AddCartSuccess(c *gin.Context) {goodsId, err := models.Int(c.Query("goods_id"))if err != nil {c.Redirect(http.StatusFound, "/")}//实例化商品goods := models.Goods{}models.DB.Where("id = ?", goodsId).Find(&goods)//跳转到添加购物车成功的模板页面var tpl = "frontend/cart/addcart_success.html"con.Render(c, tpl, gin.H{"goods": goods,})
}//增加购物车数量
func (con CartController) IncCart(c *gin.Context) {//1、获取客户端穿过来的数据goodsId, err := models.Int(c.Query("goods_id"))goodsColor := c.Query("goods_color")GoodsAttr := ""//定义返回的数据//总的选中商品价格var allPrice float64//当前商品总价var currentPrice float64var num intvar response gin.H//2、判断数据是否合法if err != nil {response = gin.H{"success": false,"message": "传入参数错误",}} else {//获取购物车数据cartList := []models.Cart{}models.Cookie.Get(c, "cartList", &cartList)if len(cartList) > 0 {for i := 0; i < len(cartList); i++ { //循环购物车,找到要增加的商品,给其数量+1if cartList[i].Id == goodsId && cartList[i].GoodsColor == goodsColor && cartList[i].GoodsAttr == GoodsAttr {cartList[i].Num = cartList[i].Num + 1currentPrice = float64(cartList[i].Num) * cartList[i].Pricenum = cartList[i].Num}//判断该商品是否选中,并计算总价格if cartList[i].Checked {allPrice += cartList[i].Price * float64(cartList[i].Num)}}//重新写入数据models.Cookie.Set(c, "cartList", cartList)response = gin.H{"success":      true,"message":      "更新数据成功","allPrice":     allPrice,"num":          num,"currentPrice": currentPrice,}} else {response = gin.H{"success": false,"message": "传入参数错误",}}}c.JSON(http.StatusOK, response)
}//减少购物车数量
func (con CartController) DecCart(c *gin.Context) {//1、获取客户端穿过来的数据goodsId, err := models.Int(c.Query("goods_id"))goodsColor := c.Query("goods_color")GoodsAttr := ""//定义返回的数据//总价var allPrice float64//当前商品的价格小计var currentPrice float64var num intvar response gin.H//2、判断数据是否合法if err != nil {response = gin.H{"success": false,"message": "传入参数错误",}} else {cartList := []models.Cart{}models.Cookie.Get(c, "cartList", &cartList)if len(cartList) > 0 {for i := 0; i < len(cartList); i++ {if cartList[i].Id == goodsId && cartList[i].GoodsColor == goodsColor && cartList[i].GoodsAttr == GoodsAttr {if cartList[i].Num > 1 {cartList[i].Num = cartList[i].Num - 1}currentPrice = float64(cartList[i].Num) * cartList[i].Pricenum = cartList[i].Num}if cartList[i].Checked {allPrice += cartList[i].Price * float64(cartList[i].Num)}}//重新写入数据models.Cookie.Set(c, "cartList", cartList)response = gin.H{"success":      true,"message":      "更新数据成功","allPrice":     allPrice,"num":          num,"currentPrice": currentPrice,}} else {response = gin.H{"success": false,"message": "传入参数错误",}}}c.JSON(http.StatusOK, response)
}//改变一个商品数据的选中状态
func (con CartController) ChangeOneCart(c *gin.Context) {//1、获取客户端传过来的数据goodsId, err := models.Int(c.Query("goods_id"))goodsColor := c.Query("goods_color")GoodsAttr := ""//定义返回的数据var allPrice float64var response gin.H//2、判断数据是否合法if err != nil {response = gin.H{"success": false,"message": "传入参数错误",}} else {//获取购物车中的商品数据cartList := []models.Cart{}models.Cookie.Get(c, "cartList", &cartList)if len(cartList) > 0 {for i := 0; i < len(cartList); i++ { // 修改其选中状态if cartList[i].Id == goodsId && cartList[i].GoodsColor == goodsColor && cartList[i].GoodsAttr == GoodsAttr {cartList[i].Checked = !cartList[i].Checked}if cartList[i].Checked { // 计算购物车商品总价格allPrice += cartList[i].Price * float64(cartList[i].Num)}}//重新写入数据models.Cookie.Set(c, "cartList", cartList)response = gin.H{"success":  true,"message":  "更新数据成功","allPrice": allPrice,}} else {response = gin.H{"success": false,"message": "传入参数错误",}}}c.JSON(http.StatusOK, response)
}//全选反选
func (con CartController) ChangeAllCart(c *gin.Context) {//获取flag标签: 1 全选, 0 反选flag, _ := models.Int(c.Query("flag"))//定义返回的数据var allPrice float64var response gin.H//获取购物车数据cartList := []models.Cart{}models.Cookie.Get(c, "cartList", &cartList)if len(cartList) > 0 {  // 判断购物车是否为空for i := 0; i < len(cartList); i++ {if flag == 1 {  // 全选cartList[i].Checked = true} else {  // 反选cartList[i].Checked = false}if cartList[i].Checked { //计算总价格allPrice += cartList[i].Price * float64(cartList[i].Num)}}//重新写入数据models.Cookie.Set(c, "cartList", cartList)response = gin.H{"success":  true,"message":  "更新数据成功","allPrice": allPrice,}} else {response = gin.H{"success": false,"message": "传入参数错误",}}c.JSON(http.StatusOK, response)
}//删除购物车数据
func (con CartController) DelCart(c *gin.Context) {//获取请求的商品id以及颜色goodsId, _ := models.Int(c.Query("goods_id"))goodsColor := c.Query("goods_color")GoodsAttr := ""//获取购物车数据cartList := []models.Cart{}models.Cookie.Get(c, "cartList", &cartList)for i := 0; i < len(cartList); i++ {//找到要删除的商品if cartList[i].Id == goodsId && cartList[i].GoodsColor == goodsColor && cartList[i].GoodsAttr == GoodsAttr {//cartList[:i]: 目标商品之前的商品数据, cartList[(i+1):]:目标商品之后的商品数据//重构购物车数据cartList = append(cartList[:i], cartList[(i+1):]...)}}//保存新的购物车数据到cookiemodels.Cookie.Set(c, "cartList", cartList)//重定向跳转c.Redirect(http.StatusFound, "/cart")
}

增加购物车相关路由

//获取购物车数据
defaultRouters.GET("/cart", frontend.CartController{}.Get)
//增加购物车数据
defaultRouters.GET("/cart/addCart", frontend.CartController{}.AddCart)
//购物车增加成功跳转页面
defaultRouters.GET("/cart/successTip", frontend.CartController{}.AddCartSuccess)
//减少购物车中商品数量
defaultRouters.GET("/cart/decCart", frontend.CartController{}.DecCart)
//增加购物车中商品数量
defaultRouters.GET("/cart/incCart", frontend.CartController{}.IncCart)
//改变一个商品数据的选中状态
defaultRouters.GET("/cart/changeOneCart", frontend.CartController{}.ChangeOneCart)
//全选反选
defaultRouters.GET("/cart/changeAllCart", frontend.CartController{}.ChangeAllCart)
//删除购物车商品数据
defaultRouters.GET("/cart/delCart", frontend.CartController{}.DelCart)

商品详情页加入购物车html以及js

增加 "加入购物车" 点击按钮,以及加入购物车 click事件
<div class="xiadan ml20 mt10"><input class="jrgwc" type="button" id="addCart" name="jrgwc" value="加入购物车"/>
</div><script>$(function () {//添加购物车数据$('#addCart').click(function () {var goods_id = $('#title').attr('goods_id');  //获取商品idvar color_id = $('#color_list .active').attr('color_id'); //获取商品颜色location.href = "/cart/addCart?goods_id=" + goods_id + '&color_id=' + color_id;})})</script>

加入购物车成功的html页面

展示商品加入购物车 成功的页面,展示对应 商品相关信息,以及 "进入购物车"按钮等
{{ define "frontend/cart/addcart_success.html" }}{{ template "frontend/public/page_header.html" .}}{{ template "frontend/public/middle_nav.html" .}}<style>.buy-succ-box {margin-bottom: 26px;padding: 40px 0 40px 0;height: 68px;border-bottom: 1px solid #e0e0e0;}.buy-succ-box .goods-content {float: left;}.buy-succ-box .actions {float: right;}.buy-succ-box .goods-img {float: left;width: 64px;height: 64px;}.buy-succ-box .goods-info {float: left;margin-left: 20px;}.buy-succ-box .goods-info h3 {margin: 0;color: #424242;font-size: 24px;font-weight: normal;margin-top: 3px;}.buy-succ-box .goods-info .name, .buy-succ-box .goods-info .price {margin-right: 15px;font-size: 14px;color: #757575;}.buy-succ-box .actions .btn {width: 180px;margin-left: 12px;margin-top: 5px;}.btn-line-gray {border-color: #b0b0b0;background: #fff;color: #757575;}.btn {display: inline-block;*zoom: 1;*display: inline;width: 158px;height: 38px;padding: 0;margin: 0;border: 1px solid #b0b0b0;font-size: 14px;line-height: 38px;text-align: center;color: #b0b0b0;cursor: pointer;-webkit-transition: all .4s;transition: all .4s;}.btn-primary {background: #ff6700;border-color: #ff6700;color: #fff;}</style><div class="container w"><div class="buy-succ-box clearfix"><div class="goods-content" id="J_goodsBox"><div class="goods-img"><img src="{{.goods.GoodsImg | FormatImg}}" width="64" height="64"></div><div class="goods-info"><h3>已成功加入购物车!</h3> <spanclass="name">{{.goods.Title}} -- {{.goods.GoodsVersion}}</span></div></div><div class="actions J_actBox"><a href="/detail?id={{.goods.Id}}" class="btn btn-line-gray J_goBack">返回上一级</a><a href="/cart" class="btn btn-primary">去购物车结算</a></div></div></div>{{ template "frontend/public/page_footer.html" .}}<!-- end danpin --></body></html>{{end}}

购物车列表页面

展示购物车里面的 商品相关数据列表,改变对应商品的 选中状态,商品数量 增加/减少,以及 删除,计算 商品小计, 总的选中商品价格, 全选/反选等功能
{{ define "frontend/cart/cart.html" }}{{ template "frontend/public/page_header.html" .}}<link rel="stylesheet" type="text/css" href="/static/frontend/css/cart.css"><script src="/static/frontend/js/cart.js"></script><!-- start banner_x --><div class="banner_x center"><a href="/"><div class="logo fl"></div></a><div class="wdgwc fl ml40">我的购物车</div><div class="wxts fl ml20">温馨提示:产品是否购买成功,以最终下单为准哦,请尽快结算</div><div class="dlzc fr"><ul><li><a href="./login.html" target="_blank">登录</a></li><li>|</li><li><a href="./register.html" target="_blank">注册</a></li></ul></div><div class="clear"></div></div><div class="xiantiao"></div><div class="gwcxqbj"><div class="gwcxd center"><table class="table"><tr class="th"><th><input type="checkbox" id="checkAll"/>全选</th><th>商品名称</th><th>单价</th><th>数量</th><th>小计</th><th>操作</th></tr>{{range $key,$value := .cartList}}<tr class="cart_list"><td><input type="checkbox" goods_id="{{$value.Id}}"goods_color="{{$value.GoodsColor}}" {{if eq $value.Checked true}} checked {{end}} /></td><td><div class="col_pic"><img src="{{$value.GoodsImg | FormatImg}}"/></div><div class="col_title">{{$value.Title}} -- {{$value.GoodsColor}} {{$value.GoodsVersion}}</div></td><td class="price">{{$value.Price}}元</td><td><div class="cart_number"><div class="input_left decCart" goods_id="{{$value.Id}}"goods_color="{{$value.GoodsColor}}">-</div><div class="input_center"><input id="num" name="num" readonly="readonly" type="text" value="{{$value.Num}}"/></div><div class="input_right incCart" goods_id="{{$value.Id}}"goods_color="{{$value.GoodsColor}}">+</div></div></td><td class="totalPrice">{{ Mul $value.Price $value.Num}}元</td><td><span><a href="/cart/delCart?goods_id={{$value.Id}}&goods_color={{$value.GoodsColor}}"class="delete"> 删除</a></span></td></tr>{{end}}</table></div><div class="jiesuandan mt20 center"><div class="tishi fl ml20"><ul><li><a href="./liebiao.html">继续购物</a></li><li>|</li><li>共<span>2</span>件商品,已选择<span>1</span>件</li><div class="clear"></div></ul></div><div class="jiesuan fr"><div class="jiesuanjiage fl">合计(不含运费):<span id="allPrice">{{.allPrice}}元</span></div><div class="jsanniu fr"><input class="jsan" type="submit" name="jiesuan" value="去结算"/></div><div class="clear"></div></div><div class="clear"></div></div></div><!-- footer -->{{ template "frontend/public/page_footer.html" .}}</body></html>
{{end}}

购物车页面js

实现 删除确认, 改变购物车数据, 改变一个商品数据的选中状态, 判断全选是否选择等效果以及功能
(function ($) {var app = {init: function () {this.changeCartNum();this.deleteConfirm();this.initCheckBox();this.isCheckedAll();},deleteConfirm: function () { //删除确认$('.delete').click(function () {var flag = confirm('您确定要删除吗?');return flag;})},changeCartNum() {  // 改变购物车数据$('.decCart').click(function () {  //减少数量//获取商品id,商品颜色var goods_id = $(this).attr("goods_id")var goods_color = $(this).attr("goods_color")var _that = this;//请求url$.get('/cart/decCart?goods_id=' + goods_id + '&goods_color=' + goods_color, function (response) {if (response.success) { //改变成功//更新总商品价格$("#allPrice").html(response.allPrice + "元")//更新对应商品数量$(_that).siblings(".input_center").find("input").val(response.num)//更新对应商品价格小计$(_that).parent().parent().siblings(".totalPrice").html(response.currentPrice + "元")}})});$('.incCart').click(function () {  // 增加数量var goods_id = $(this).attr("goods_id")var goods_color = $(this).attr("goods_color")var _that = this;$.get('/cart/incCart?goods_id=' + goods_id + '&goods_color=' + goods_color, function (response) {console.log(response)if (response.success) {$("#allPrice").html(response.allPrice + "元")$(_that).siblings(".input_center").find("input").val(response.num)$(_that).parent().parent().siblings(".totalPrice").html(response.currentPrice + "元")}})});},initCheckBox(){  //改变一个商品数据的选中状态//全选按钮点击$("#checkAll").click(function() {if (this.checked) {$(":checkbox").prop("checked", true);//让cookie中商品的checked属性都等于true$.get('/cart/changeAllCart?flag=1',function(response){if(response.success){$("#allPrice").html(response.allPrice+"元")}})}else {$(":checkbox").prop("checked", false);//让cookie中商品的checked属性都等于false$.get('/cart/changeAllCart?flag=0',function(response){if(response.success){$("#allPrice").html(response.allPrice+"元")}})}});//点击单个选择框按钮的时候触发var _that=this;$(".cart_list :checkbox").click(function() {_that.isCheckedAll();var goods_id=$(this).attr("goods_id")var goods_color=$(this).attr("goods_color")$.get('/cart/changeOneCart?goods_id='+goods_id+'&goods_color='+goods_color,function(response){if(response.success){$("#allPrice").html(response.allPrice+"元")}})});   //注意:this指向},isCheckedAll(){   //判断全选是否选择var allNum = $(".cart_list :checkbox").size();//checkbox总个数var checkedNum = 0;$(".cart_list :checkbox").each(function () {if($(this).prop("checked")==true){checkedNum++;}});if(allNum==checkedNum){//全选$("#checkAll").prop("checked",true);}else{//不全选$("#checkAll").prop("checked",false);}},}$(function () {app.init();})
})($)

[上一节][golang gin框架] 26.Gin 商城项目-前台自定义商品列表模板, 商品详情数据渲染,Markdown语法使用

[下一节][golang gin框架] 28.Gin 发送短信,DES加密解,Cookie加密,解密操作

本文标签: golang gin框架 27Gin 商城项目