go-商品服务-web第二节-增删改查
一.新建商品
1. 配置路由
goods-web/router/goods.go
package router
import (
"github.com/gin-gonic/gin"
"mxshop-api/goods-web/api/goods"
)
func InitGoodsRoute(Router *gin.RouterGroup) {
GoodsRouter := Router.Group("goods")
{
GoodsRouter.GET("list", goods.List)
//GoodsRouter.GET("new", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New)
GoodsRouter.POST("new", goods.New)
}
}
2.配置form验证
goods-web/forms/goods.go
package forms
type GoodsForm struct {
Name string `form:"name" json:"name" binding:"required,min=2,max=100"`
GoodsSn string `form:"goods_sn" json:"goods_sn" binding:"required,min=2,lt=20"`
Stocks int32 `form:"stocks" json:"stocks" binding:"required,min=1"`
CategoryId int32 `form:"category" json:"category" binding:"required"`
MarketPrice float32 `form:"market_price" json:"market_price" binding:"required,min=0"`
ShopPrice float32 `form:"shop_price" json:"shop_price" binding:"required,min=0"`
GoodsBrief string `form:"goods_brief" json:"goods_brief" binding:"required,min=3"`
Images []string `form:"images" json:"images" binding:"required,min=1"`
DescImages []string `form:"desc_images" json:"desc_images" binding:"required,min=1"`
ShipFree *bool `form:"ship_free" json:"ship_free" binding:"required"`
FrontImage string `form:"front_image" json:"front_image" binding:"required,url"`
Brand int32 `form:"brand" json:"brand" binding:"required"`
}
2.配置函数
goods-web/api/goods/goods.go
func New(ctx *gin.Context) {
goodsForm := forms.GoodsForm{}
if err := ctx.ShouldBindJSON(&goodsForm); err != nil {
HandleValidatorError(ctx, err)
return
}
goodsClient := global.GoodsSrvClient
rsp, err := goodsClient.CreateGoods(context.Background(), &proto.CreateGoodsInfo{
Name: goodsForm.Name,
GoodsSn: goodsForm.GoodsSn,
Stocks: goodsForm.Stocks,
MarketPrice: goodsForm.MarketPrice,
ShopPrice: goodsForm.ShopPrice,
GoodsBrief: goodsForm.GoodsBrief,
ShipFree: *goodsForm.ShipFree,
Images: goodsForm.Images,
DescImages: goodsForm.DescImages,
GoodsFrontImage: goodsForm.FrontImage,
CategoryId: goodsForm.CategoryId,
BrandId: goodsForm.Brand,
})
if err != nil {
HandleGrpcErrorToHttp(err, ctx)
return
}
//如何设置库存
//TODO 商品的库存 - 分布式事务
ctx.JSON(http.StatusOK, rsp)
}
二.商品详情
1.路由配置
package router
import (
"github.com/gin-gonic/gin"
"mxshop-api/goods-web/api/goods"
)
func InitGoodsRoute(Router *gin.RouterGroup) {
GoodsRouter := Router.Group("goods")
{
GoodsRouter.GET("", goods.List) //商品列表
//GoodsRouter.GET("new", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New)
GoodsRouter.POST("", goods.New)//商品新建
//GoodsRouter.GET("list", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.List)
GoodsRouter.GET("/:id", goods.Detail)//商品详情
}
}
2.配置函数
goods-web/api/goods/goods.go
func Detail(ctx *gin.Context) {
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
r, err := global.GoodsSrvClient.GetGoodsDetail(context.WithValue(context.Background(), "ginContext", ctx), &proto.GoodInfoRequest{
Id: int32(i),
})
if err != nil {
HandleGrpcErrorToHttp(err, ctx)
return
}
rsp := map[string]interface{}{
"id": r.Id,
"name": r.Name,
"goods_brief": r.GoodsBrief,
"desc": r.GoodsDesc,
"ship_free": r.ShipFree,
"images": r.Images,
"desc_images": r.DescImages,
"front_image": r.GoodsFrontImage,
"shop_price": r.ShopPrice,
"ctegory": map[string]interface{}{
"id": r.Category.Id,
"name": r.Category.Name,
},
"brand": map[string]interface{}{
"id": r.Brand.Id,
"name": r.Brand.Name,
"logo": r.Brand.Logo,
},
"is_hot": r.IsHot,
"is_new": r.IsNew,
"on_sale": r.OnSale,
}
ctx.JSON(http.StatusOK, rsp)
}
三.删除和更新商品
1.路由配置
package router
import (
"github.com/gin-gonic/gin"
"mxshop-api/goods-web/api/goods"
)
func InitGoodsRoute(Router *gin.RouterGroup) {
GoodsRouter := Router.Group("goods")
{
GoodsRouter.GET("", goods.List) //商品列表
//GoodsRouter.GET("new", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New)
GoodsRouter.POST("", goods.New) //商品新建
//GoodsRouter.GET("list", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.List)
GoodsRouter.GET("/:id", goods.Detail) //商品详情
GoodsRouter.DELETE("/:id", goods.Delete) //删除商品
GoodsRouter.PUT("/:id", goods.Update) // 更新商品
GoodsRouter.PATCH("/:id", goods.UpdateStatus) // 部分更新
}
}
2.配置函数
func Delete(ctx *gin.Context) {
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
_, err = global.GoodsSrvClient.DeleteGoods(context.Background(), &proto.DeleteGoodsInfo{Id: int32(i)})
if err != nil {
HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
return
}
func Update(ctx *gin.Context) {
goodsForm := forms.GoodsForm{}
if err := ctx.ShouldBindJSON(&goodsForm); err != nil {
HandleValidatorError(ctx, err)
return
}
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if _, err = global.GoodsSrvClient.UpdateGoods(context.Background(), &proto.CreateGoodsInfo{
Id: int32(i),
Name: goodsForm.Name,
GoodsSn: goodsForm.GoodsSn,
Stocks: goodsForm.Stocks,
MarketPrice: goodsForm.MarketPrice,
ShopPrice: goodsForm.ShopPrice,
GoodsBrief: goodsForm.GoodsBrief,
ShipFree: *goodsForm.ShipFree,
Images: goodsForm.Images,
DescImages: goodsForm.DescImages,
GoodsFrontImage: goodsForm.FrontImage,
CategoryId: goodsForm.CategoryId,
BrandId: goodsForm.Brand,
}); err != nil {
HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.JSON(http.StatusOK, gin.H{
"msg": "更新成功",
})
}
func UpdateStatus(ctx *gin.Context) {
goodsStatusForm := forms.GoodsStatusForm{}
if err := ctx.ShouldBindJSON(&goodsStatusForm); err != nil {
HandleValidatorError(ctx, err)
return
}
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if _, err = global.GoodsSrvClient.UpdateGoods(context.Background(), &proto.CreateGoodsInfo{
Id: int32(i),
IsHot: *goodsStatusForm.IsNew,
IsNew: *goodsStatusForm.IsNew,
OnSale: *goodsStatusForm.OnSale,
}); err != nil {
HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.JSON(http.StatusOK, gin.H{
"msg": "修改成功",
})
}
3.部分更新的form表单
type GoodsStatusForm struct {
IsNew *bool `form:"new" json:"new" binding:"required"`
IsHot *bool `form:"hot" json:"hot" binding:"required"`
OnSale *bool `form:"sale" json:"sale" binding:"required"`
}
三.商品分类
1. 设置api base内容
新建goods-web/api/base.go

增加内容
package api
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"mxshop-api/goods-web/global"
)
func RemoveTopStruct(fileds map[string]string) map[string]string {
rsp := map[string]string{}
for field, err := range fileds {
rsp[field[strings.Index(field, ".")+1:]] = err
}
return rsp
}
func HandleGrpcErrorToHttp(err error, c *gin.Context) {
// 将GRPC的code转换成HTTP的状态码
if err != nil {
if e, ok := status.FromError(err); ok {
switch e.Code() {
case codes.NotFound:
c.JSON(http.StatusNotFound, gin.H{
"msg": e.Message(),
})
case codes.Internal:
c.JSON(http.StatusInternalServerError, gin.H{
"msg": "内部错误",
})
case codes.InvalidArgument:
c.JSON(http.StatusBadRequest, gin.H{
"msg": "参数错误",
})
case codes.Unavailable:
c.JSON(http.StatusInternalServerError, gin.H{
"msg": "用户服务不可用",
})
default:
c.JSON(http.StatusInternalServerError, gin.H{
"msg": "其他错误",
})
}
return
}
}
}
func HandleValidatorError(ctx *gin.Context, err error) {
//定义统一返回的报错处理
errs, ok := err.(validator.ValidationErrors)
if !ok {
ctx.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
}
ctx.JSON(http.StatusBadRequest, gin.H{
"error": RemoveTopStruct(errs.Translate(global.Trans)),
})
return
}
2. 设置路由
新建文件:goods-web/router/category.go
package router
import (
"github.com/gin-gonic/gin"
"mxshop-api/goods-web/api/category"
)
func InitCategoryRouter(Router *gin.RouterGroup) {
//CategoryRouter := Router.Group("categorys").Use(middlewares.Trace())
CategoryRouter := Router.Group("categorys")
{
CategoryRouter.GET("", category.List) // 商品类别列表页
CategoryRouter.DELETE("/:id", category.Delete) // 删除分类
CategoryRouter.GET("/:id", category.Detail) // 获取分类详情
CategoryRouter.POST("", category.New) //新建分类
CategoryRouter.PUT("/:id", category.Update) //修改分类信息
}
}
3.代码
新建文件:goods-web/api/category/category.go
package category
import (
"context"
"encoding/json"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
empty "github.com/golang/protobuf/ptypes/empty"
"go.uber.org/zap"
"mxshop-api/goods-web/api"
"mxshop-api/goods-web/forms"
"mxshop-api/goods-web/global"
"mxshop-api/goods-web/proto"
)
func List(ctx *gin.Context) {
r, err := global.GoodsSrvClient.GetAllCategorysList(context.Background(), &empty.Empty{})
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
data := make([]interface{}, 0)
err = json.Unmarshal([]byte(r.JsonData), &data)
if err != nil {
zap.S().Errorw("[List] 查询 【分类列表】失败: ", err.Error())
}
ctx.JSON(http.StatusOK, data)
}
func Detail(ctx *gin.Context) {
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
reMap := make(map[string]interface{})
subCategorys := make([]interface{}, 0)
if r, err := global.GoodsSrvClient.GetSubCategory(context.Background(), &proto.CategoryListRequest{
Id: int32(i),
}); err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
} else {
//写文档 特别是数据多的时候很慢, 先开发后写文档
for _, value := range r.SubCategorys {
subCategorys = append(subCategorys, map[string]interface{}{
"id": value.Id,
"name": value.Name,
"level": value.Level,
"parent_category": value.ParentCategory,
"is_tab": value.IsTab,
})
}
reMap["id"] = r.Info.Id
reMap["name"] = r.Info.Name
reMap["level"] = r.Info.Level
reMap["parent_category"] = r.Info.ParentCategory
reMap["is_tab"] = r.Info.IsTab
reMap["sub_categorys"] = subCategorys
ctx.JSON(http.StatusOK, reMap)
}
return
}
func New(ctx *gin.Context) {
categoryForm := forms.CategoryForm{}
if err := ctx.ShouldBindJSON(&categoryForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
rsp, err := global.GoodsSrvClient.CreateCategory(context.Background(), &proto.CategoryInfoRequest{
Name: categoryForm.Name,
IsTab: *categoryForm.IsTab,
Level: categoryForm.Level,
ParentCategory: categoryForm.ParentCategory,
})
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
request := make(map[string]interface{})
request["id"] = rsp.Id
request["name"] = rsp.Name
request["parent"] = rsp.ParentCategory
request["level"] = rsp.Level
request["is_tab"] = rsp.IsTab
ctx.JSON(http.StatusOK, request)
}
func Delete(ctx *gin.Context) {
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
//1. 先查询出该分类写的所有子分类
//2. 将所有的分类全部逻辑删除
//3. 将该分类下的所有的商品逻辑删除
_, err = global.GoodsSrvClient.DeleteCategory(context.Background(), &proto.DeleteCategoryRequest{Id: int32(i)})
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
}
func Update(ctx *gin.Context) {
categoryForm := forms.UpdateCategoryForm{}
if err := ctx.ShouldBindJSON(&categoryForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
request := &proto.CategoryInfoRequest{
Id: int32(i),
Name: categoryForm.Name,
}
if categoryForm.IsTab != nil {
request.IsTab = *categoryForm.IsTab
}
_, err = global.GoodsSrvClient.UpdateCategory(context.Background(), request)
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
}
四.轮播图
1.form表单
新建:goods-web/forms/banner.go
package forms
type BannerForm struct {
Image string `form:"image" json:"image" binding:"url"`
Index int `form:"index" json:"index" binding:"required"`
Url string `form:"url" json:"url" binding:"url"`
}
2.函数
新建:goods-web/api/banner/banner.go
package banners
import (
"context"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/golang/protobuf/ptypes/empty"
"mxshop-api/goods-web/api"
"mxshop-api/goods-web/forms"
"mxshop-api/goods-web/global"
"mxshop-api/goods-web/proto"
)
func List(ctx *gin.Context) {
rsp, err := global.GoodsSrvClient.BannerList(context.Background(), &empty.Empty{})
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
result := make([]interface{}, 0)
for _, value := range rsp.Data {
reMap := make(map[string]interface{})
reMap["id"] = value.Id
reMap["index"] = value.Index
reMap["image"] = value.Image
reMap["url"] = value.Url
result = append(result, reMap)
}
ctx.JSON(http.StatusOK, result)
}
func New(ctx *gin.Context) {
bannerForm := forms.BannerForm{}
if err := ctx.ShouldBindJSON(&bannerForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
rsp, err := global.GoodsSrvClient.CreateBanner(context.Background(), &proto.BannerRequest{
Index: int32(bannerForm.Index),
Url: bannerForm.Url,
Image: bannerForm.Image,
})
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
response := make(map[string]interface{})
response["id"] = rsp.Id
response["index"] = rsp.Index
response["url"] = rsp.Url
response["image"] = rsp.Image
ctx.JSON(http.StatusOK, response)
}
func Update(ctx *gin.Context) {
bannerForm := forms.BannerForm{}
if err := ctx.ShouldBindJSON(&bannerForm); err != nil {
api.HandleValidatorError(ctx, err)
return
}
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
_, err = global.GoodsSrvClient.UpdateBanner(context.Background(), &proto.BannerRequest{
Id: int32(i),
Index: int32(bannerForm.Index),
Url: bannerForm.Url,
})
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.Status(http.StatusOK)
}
func Delete(ctx *gin.Context) {
id := ctx.Param("id")
i, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
_, err = global.GoodsSrvClient.DeleteBanner(context.Background(), &proto.BannerRequest{Id: int32(i)})
if err != nil {
api.HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.JSON(http.StatusOK, "")
}
3. 配置路由
创建文件:goods-web/router/banner.go
package router
import (
"github.com/gin-gonic/gin"
"mxshop-api/goods-web/api/banners"
"mxshop-api/goods-web/middlewares"
)
func InitBannerRouter(Router *gin.RouterGroup) {
BannerRouter := Router.Group("banners").Use(middlewares.Trace())
{
BannerRouter.GET("", banners.List) // 轮播图列表页
BannerRouter.DELETE("/:id", middlewares.JWTAuth(), middlewares.IsAdminAuth(), banners.Delete) // 删除轮播图
BannerRouter.POST("", middlewares.JWTAuth(), middlewares.IsAdminAuth(), banners.New) //新建轮播图
BannerRouter.PUT("/:id", middlewares.JWTAuth(), middlewares.IsAdminAuth(), banners.Update) //修改轮播图信息
}
}
五.品牌分类
1.form表单
创建:goods-web/forms/brand.go
package forms
type BrandForm struct {
Name string `form:"name" json:"name" binding:"required,min=3,max=10"`
Logo string `form:"logo" json:"logo" binding:"url"`
}
type CategoryBrandForm struct {
CategoryId int `form:"category_id" json:"category_id" binding:"required"`
BrandId int `form:"brand_id" json:"brand_id" binding:"required"`
}
2.代码
创建:goods-web/api/brands/brand.go
package router
import (
"github.com/gin-gonic/gin"
"mxshop-api/goods-web/api/brands"
)
// 1. 商品的api接口开发完成
// 2. 图片的坑
func InitBrandRouter(Router *gin.RouterGroup) {
BrandRouter := Router.Group("brands")
{
BrandRouter.GET("", brands.BrandList) // 品牌列表页
BrandRouter.DELETE("/:id", brands.DeleteBrand) // 删除品牌
BrandRouter.POST("", brands.NewBrand) //新建品牌
BrandRouter.PUT("/:id", brands.UpdateBrand) //修改品牌信息
}
CategoryBrandRouter := Router.Group("categorybrands")
{
CategoryBrandRouter.GET("", brands.CategoryBrandList) // 类别品牌列表页
CategoryBrandRouter.DELETE("/:id", brands.DeleteCategoryBrand) // 删除类别品牌
CategoryBrandRouter.POST("", brands.NewCategoryBrand) //新建类别品牌
CategoryBrandRouter.PUT("/:id", brands.UpdateCategoryBrand) //修改类别品牌
CategoryBrandRouter.GET("/:id", brands.GetCategoryBrandList) //获取分类的品牌
}
}
3.路由
创建:goods-web/router/brand.go
package router
import (
"github.com/gin-gonic/gin"
"mxshop-api/goods-web/api/brands"
)
// 1. 商品的api接口开发完成
// 2. 图片的坑
func InitBrandRouter(Router *gin.RouterGroup) {
BrandRouter := Router.Group("brands")
{
BrandRouter.GET("", brands.BrandList) // 品牌列表页
BrandRouter.DELETE("/:id", brands.DeleteBrand) // 删除品牌
BrandRouter.POST("", brands.NewBrand) //新建品牌
BrandRouter.PUT("/:id", brands.UpdateBrand) //修改品牌信息
}
CategoryBrandRouter := Router.Group("categorybrands")
{
CategoryBrandRouter.GET("", brands.CategoryBrandList) // 类别品牌列表页
CategoryBrandRouter.DELETE("/:id", brands.DeleteCategoryBrand) // 删除类别品牌
CategoryBrandRouter.POST("", brands.NewCategoryBrand) //新建类别品牌
CategoryBrandRouter.PUT("/:id", brands.UpdateCategoryBrand) //修改类别品牌
CategoryBrandRouter.GET("/:id", brands.GetCategoryBrandList) //获取分类的品牌
}
}

浙公网安备 33010602011771号