• 25、订单和购物车-web服务


    一、快速启动order_web项目

    • 将之前的goods_web项目的文件拷贝到order_web目录下
    • 选择路径 D:\GOProject\mxshop_srvs\mxshop_api\order_web 替换:将 goods_web 全部替换为 order_web
    • 添加 order_web 的 nacos配置
    {
      "host": "192.168.78.1",
      "name": "order_web",
      "port": 8083,
      "tags": ["mxshop","imooc","bobby","order","web"],
      "goods_srv": {
        "name": "goods_srv"
      },
      "order_srv": {
        "name": "order_srv"
      },
      "jwt": {
        "key": "VYLDYq3&hGWjWqF$K1ih"
      },
      "consul": {
        "host": "192.168.78.131",
        "port": 8500
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    • 修改api、router、proto:具体的查看源码

    在这里插入图片描述


    二、购物车api接口

    1 - 测试中间件

    • order_web/middlewares/test_set_userid.go:这里为了测试效率去掉了jwt的验证,并且直接指定userId = 1 来进行接口测试
    package middlewares
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    func SetUserId() gin.HandlerFunc {
    	return func(ctx *gin.Context) {
    		var userId uint = 1
    		ctx.Set("userId", userId)
    		ctx.Next()
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • order_web/router/router_shop_cart.go:添加设置userId的中间件
    package router
    
    import (
    	"github.com/gin-gonic/gin"
    	"web_api/order_web/api/shop_cart"
    	"web_api/order_web/middlewares"
    )
    
    func InitShopCartRouter(Router *gin.RouterGroup) {
    	//ShopCartRouter := Router.Group("shopcarts").Use(middlewares.JWTAuth()) // 测试删除jwt验证
    	ShopCartRouter := Router.Group("shopcarts").Use(middlewares.SetUserId())
    	{
    		ShopCartRouter.GET("", shop_cart.List)          //购物车列表
    		ShopCartRouter.DELETE("/:id", shop_cart.Delete) //删除条目
    		ShopCartRouter.POST("", shop_cart.New)          //添加商品到购物车
    		ShopCartRouter.PATCH("/:id", shop_cart.Update)  //修改条目
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2 - 订单列表接口

    • order_web/api/shop_cart/api_shop_cart.go
    func List(ctx *gin.Context) {
    	//获取购物车商品
    	userId, _ := ctx.Get("userId")
    	fmt.Println(userId)
    	rsp, err := global.OrderSrvClient.CartItemList(context.Background(), &proto.UserInfo{
    		Id: int32(userId.(uint)),
    	})
    	if err != nil {
    		zap.S().Errorw("[List] 查询 【购物车列表】失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    
    	ids := make([]int32, 0)
    	for _, item := range rsp.Data {
    		ids = append(ids, item.GoodsId)
    	}
    	if len(ids) == 0 {
    		ctx.JSON(http.StatusOK, gin.H{
    			"total": 0,
    		})
    		return
    	}
    
    	//请求商品服务获取商品信息
    	goodsRsp, err := global.GoodsSrvClient.BatchGetGoods(context.Background(), &proto.BatchGoodsIdInfo{
    		Id: ids,
    	})
    	if err != nil {
    		zap.S().Errorw("[List] 批量查询【商品列表】失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    
    	reMap := gin.H{
    		"total": rsp.Total,
    	}
    
    	/*
    		{
    			"total":12,
    			"data":[
    				{
    					"id":1,
    					"goods_id":421,
    					"goods_name":421,
    					"goods_price":421,
    					"goods_image":421,
    					"nums":421,
    					"checked":421,
    				}
    			]
    		}
    	*/
    	goodsList := make([]interface{}, 0)
    	for _, item := range rsp.Data {
    		for _, good := range goodsRsp.Data {
    			if good.Id == item.GoodsId {
    				tmpMap := map[string]interface{}{}
    				tmpMap["id"] = item.Id
    				tmpMap["goods_id"] = item.GoodsId
    				tmpMap["good_name"] = good.Name
    				tmpMap["good_image"] = good.GoodsFrontImage
    				tmpMap["good_price"] = good.ShopPrice
    				tmpMap["nums"] = item.Nums
    				tmpMap["checked"] = item.Checked
    				goodsList = append(goodsList, tmpMap)
    			}
    		}
    	}
    	reMap["data"] = goodsList
    	ctx.JSON(http.StatusOK, reMap)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • YApi测试
      在这里插入图片描述

    3 - 添加商品到购物车接口

    • order_web/api/shop_cart/api_shop_cart.go
    func New(ctx *gin.Context) {
    	//添加商品到购物车
    	itemForm := forms.ShopCartItemForm{}
    	if err := ctx.ShouldBindJSON(&itemForm); err != nil {
    		api.HandleValidatorError(ctx, err)
    		return
    	}
    
    	//为了严谨性,添加商品到购物车之前,记得检查一下商品是否存在
    	_, err := global.GoodsSrvClient.GetGoodsDetail(context.Background(), &proto.GoodInfoRequest{
    		Id: itemForm.GoodsId,
    	})
    	if err != nil {
    		zap.S().Errorw("[List] 查询【商品信息】失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    
    	//如果现在添加到购物车的数量和库存的数量不一致
    	invRsp, err := global.InventorySrvClient.InvDetail(context.Background(), &proto.GoodsInvInfo{
    		GoodsId: itemForm.GoodsId,
    	})
    	if err != nil {
    		zap.S().Errorw("[List] 查询【库存信息】失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    	if invRsp.Num < itemForm.Nums {
    		ctx.JSON(http.StatusBadRequest, gin.H{
    			"nums": "库存不足",
    		})
    		return
    	}
    
    	userId, _ := ctx.Get("userId")
    	rsp, err := global.OrderSrvClient.CreateCartItem(context.Background(), &proto.CartItemRequest{
    		GoodsId: itemForm.GoodsId,
    		UserId:  int32(userId.(uint)),
    		Nums:    itemForm.Nums,
    	})
    
    	if err != nil {
    		zap.S().Errorw("添加到购物车失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    
    	ctx.JSON(http.StatusOK, gin.H{
    		"id": rsp.Id,
    	})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • YApi
      在这里插入图片描述
      在这里插入图片描述

    4 - 删除购物车记录

    • order_web/api/shop_cart/api_shop_cart.go
    func Delete(ctx *gin.Context) {
    	id := ctx.Param("id")
    	i, err := strconv.Atoi(id)
    	if err != nil {
    		ctx.JSON(http.StatusNotFound, gin.H{
    			"msg": "url格式出错",
    		})
    		return
    	}
    
    	userId, _ := ctx.Get("userId")
    	_, err = global.OrderSrvClient.DeleteCartItem(context.Background(), &proto.CartItemRequest{
    		UserId:  int32(userId.(uint)),
    		GoodsId: int32(i),
    	})
    	if err != nil {
    		zap.S().Errorw("删除购物车记录失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    	ctx.Status(http.StatusOK)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    5 - 更新购物车

    • order_web/api/shop_cart/api_shop_cart.go
    func Update(ctx *gin.Context) {
    	// o/v1/421
    	id := ctx.Param("id")
    	i, err := strconv.Atoi(id)
    	if err != nil {
    		ctx.JSON(http.StatusNotFound, gin.H{
    			"msg": "url格式出错",
    		})
    		return
    	}
    
    	itemForm := forms.ShopCartItemUpdateForm{}
    	if err := ctx.ShouldBindJSON(&itemForm); err != nil {
    		api.HandleValidatorError(ctx, err)
    		return
    	}
    
    	userId, _ := ctx.Get("userId")
    	request := proto.CartItemRequest{
    		UserId:  int32(userId.(uint)),
    		GoodsId: int32(i),
    		Nums:    itemForm.Nums,
    		Checked: false,
    	}
    	if itemForm.Checked != nil {
    		request.Checked = *itemForm.Checked
    	}
    
    	_, err = global.OrderSrvClient.UpdateCartItem(context.Background(), &request)
    	if err != nil {
    		zap.S().Errorw("更新购物车记录失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    	ctx.Status(http.StatusOK)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在这里插入图片描述


    三、订单api接口

    1 - 订单列表

    • order_web/middlewares/test_set_userid.go:中间件中添加上claims的验证
    package middlewares
    
    import (
    	"github.com/gin-gonic/gin"
    	"web_api/order_web/models"
    )
    
    func SetUserId() gin.HandlerFunc {
    	return func(ctx *gin.Context) {
    		var userId uint = 1
    		ctx.Set("claims", &models.CustomClaims{
    			AuthorityId: 1,
    		})
    		ctx.Set("userId", userId)
    		ctx.Next()
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • order_web/api/order/api_order.go
    func List(ctx *gin.Context) {
    	//订单的列表
    	userId, _ := ctx.Get("userId")
    	claims, _ := ctx.Get("claims")
    
    	request := proto.OrderFilterRequest{}
    
    	//如果是管理员用户则返回所有的订单
    	model := claims.(*models.CustomClaims)
    	if model.AuthorityId == 1 {
    		request.UserId = int32(userId.(uint))
    	}
    
    	pages := ctx.DefaultQuery("p", "0")
    	pagesInt, _ := strconv.Atoi(pages)
    	request.Pages = int32(pagesInt)
    
    	perNums := ctx.DefaultQuery("pnum", "0")
    	perNumsInt, _ := strconv.Atoi(perNums)
    	request.PagePerNums = int32(perNumsInt)
    
    	request.Pages = int32(pagesInt)
    	request.PagePerNums = int32(perNumsInt)
    
    	rsp, err := global.OrderSrvClient.OrderList(context.Background(), &request)
    	if err != nil {
    		zap.S().Errorw("获取订单列表失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    
    	/*
    		{
    			"total":100,
    			"data":[
    				{
    					"
    				}
    			]
    		}
    	*/
    	reMap := gin.H{
    		"total": rsp.Total,
    	}
    	orderList := make([]interface{}, 0)
    
    	for _, item := range rsp.Data {
    		tmpMap := map[string]interface{}{}
    
    		tmpMap["id"] = item.Id
    		tmpMap["status"] = item.Status
    		tmpMap["pay_type"] = item.PayType
    		tmpMap["user"] = item.UserId
    		tmpMap["post"] = item.Post
    		tmpMap["total"] = item.Total
    		tmpMap["address"] = item.Address
    		tmpMap["name"] = item.Name
    		tmpMap["mobile"] = item.Mobile
    		tmpMap["order_sn"] = item.OrderSn
    		tmpMap["id"] = item.Id
    		tmpMap["add_time"] = item.AddTime
    
    		orderList = append(orderList, tmpMap)
    	}
    	reMap["data"] = orderList
    	ctx.JSON(http.StatusOK, reMap)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    在这里插入图片描述

    2 - 订单详情

    • order_web/api/order/api_order.go
    func Detail(ctx *gin.Context) {
    	id := ctx.Param("id")
    	userId, _ := ctx.Get("userId")
    	i, err := strconv.Atoi(id)
    	if err != nil {
    		ctx.JSON(http.StatusNotFound, gin.H{
    			"msg": "url格式出错",
    		})
    		return
    	}
    
    	//如果是管理员用户则返回所有的订单
    	request := proto.OrderRequest{
    		Id: int32(i),
    	}
    	claims, _ := ctx.Get("claims")
    	model := claims.(*models.CustomClaims)
    	if model.AuthorityId == 1 {
    		request.UserId = int32(userId.(uint))
    	}
    
    	rsp, err := global.OrderSrvClient.OrderDetail(context.Background(), &request)
    	if err != nil {
    		zap.S().Errorw("获取订单详情失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    	reMap := gin.H{}
    	reMap["id"] = rsp.OrderInfo.Id
    	reMap["status"] = rsp.OrderInfo.Status
    	reMap["user"] = rsp.OrderInfo.UserId
    	reMap["post"] = rsp.OrderInfo.Post
    	reMap["total"] = rsp.OrderInfo.Total
    	reMap["address"] = rsp.OrderInfo.Address
    	reMap["name"] = rsp.OrderInfo.Name
    	reMap["mobile"] = rsp.OrderInfo.Mobile
    	reMap["pay_type"] = rsp.OrderInfo.PayType
    	reMap["order_sn"] = rsp.OrderInfo.OrderSn
    
    	goodsList := make([]interface{}, 0)
    	for _, item := range rsp.Goods {
    		tmpMap := gin.H{
    			"id":    item.GoodsId,
    			"name":  item.GoodsName,
    			"image": item.GoodsImage,
    			"price": item.GoodsPrice,
    			"nums":  item.Nums,
    		}
    		goodsList = append(goodsList, tmpMap)
    	}
    	reMap["goods"] = goodsList
    
    	ctx.JSON(http.StatusOK, reMap)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    在这里插入图片描述

    3 - 新建订单

    • order_web/forms/form_order.go:新建表单form
    package forms
    
    type CreateOrderForm struct {
    	Address string `json:"address" binding:"required"`
    	Name    string `json:"name" binding:"required"`
    	Mobile  string `json:"mobile" binding:"required,mobile"`
    	Post    string `json:"post" binding:"required"`
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • order_web/api/order/api_order.go
    func New(ctx *gin.Context) {
    	orderForm := forms.CreateOrderForm{}
    	if err := ctx.ShouldBindJSON(&orderForm); err != nil {
    		api.HandleValidatorError(ctx, err)
    	}
    	userId, _ := ctx.Get("userId")
    	rsp, err := global.OrderSrvClient.CreateOrder(context.Background(), &proto.OrderRequest{
    		UserId:  int32(userId.(uint)),
    		Name:    orderForm.Name,
    		Mobile:  orderForm.Mobile,
    		Address: orderForm.Address,
    		Post:    orderForm.Post,
    	})
    	if err != nil {
    		zap.S().Errorw("新建订单失败")
    		api.HandleGrpcErrorToHttp(err, ctx)
    		return
    	}
    	// todo 还需要返回支付宝的支付链接,后续再实现
    	ctx.JSON(http.StatusOK, gin.H{
    		"id": rsp.Id,
    	})
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述


    四、完整源码

    • 完整源码下载mxshop_srvsV8.9.rar
    • 源码说明:(nacos的ip配置自行修改,全局变量DEV_CONFIG设置:1=zsz,2=comp,3=home)
      • goods_srv/model/sql/mxshop_goods.sql:包含了建表语句
      • other_import/api.json:YApi的导入文件
      • other_import/nacos_config_export_user.zip:nacos的user配置集导入文件
      • other_import/nacos_config_export_goods.zip:nacos的goods配置集导入文件
      • other_import/nacos_config_export_inventory.zip:nacos的inventory的配置导入文件
      • other_import/nacos_config_export_orders.zip:nacos的orders的配置导入文件
  • 相关阅读:
    SQLAlchemy学习-5.relationship之backref和back_populates参数
    uniapp directive 在原生 wgt 包不生效 uniapp directive 不生效
    【Unity/XLua】xlua自带教程示例分析(6)—— lua协程
    【Python零基础入门篇 · 36】:greenlet协程模块的使用、gevent模块的使用、程序打补丁、总结
    使用GitHub Actions和GitHub pages实现前端项目的自动打包部署
    计算机毕设 flink大数据淘宝用户行为数据实时分析与可视化
    删除vxe-table右上角的工具栏
    unity生成模型预览图并导出图片
    如何选择报修管理系统?报修工单管理系统有哪些功能和优势?
    什么是WebRTC?
  • 原文地址:https://blog.csdn.net/qq23001186/article/details/126295830