目录
一、快速启动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
}
}
- 修改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()
}
}
- 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) //修改条目
}
}
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)
}
- 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,
})
}
- 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)
}
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)
}
三、订单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()
}
}
- 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)
}
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)
}
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"`
}
- 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,
})
}
四、完整源码
- 完整源码下载: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的配置导入文件