【Go】十八、http 调用服务的编写

ops/2025/2/28 7:09:56/
http://www.w3.org/2000/svg" style="display: none;">

http_0">http接口框架的搭建

这个http接口框架的搭建参考之前的全量搭建,这里是快速搭建的模式:

直接对已有的http模块进行复制修改,主要修改点在于 proto部分与api、router 部分,剩余的要针对进行修改模块名称。

接口的具体编写

在 api/goods/goods.go 中编写具体的代码:

例如:goods的List接口的开发:

func List(ctx *gin.Context) {request := &proto.GoodsFilterRequest{}priceMin := ctx.DefaultQuery("pmin", "0") // 取出设定的最小价格,默认为 0priceMinInt, _ := strconv.Atoi(priceMin)request.PriceMin = int32(priceMinInt)priceMax := ctx.DefaultQuery("pmax", "0")priceMaxInt, _ := strconv.Atoi(priceMax)request.PriceMax = int32(priceMaxInt)isHot := ctx.DefaultQuery("ih", "0")if isHot == "1" {request.IsHot = true}isNew := ctx.DefaultQuery("in", "0")if isNew == "1" {request.IsNew = true}isTab := ctx.DefaultQuery("it", "0")if isTab == "1" {request.IsTab = true}categoryId := ctx.DefaultQuery("c", "0")categoryIdInt, _ := strconv.Atoi(categoryId)request.TopCategory = int32(categoryIdInt)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)keywords := ctx.DefaultQuery("q", "")request.KeyWords = keywordsbrandId := ctx.DefaultQuery("b", "0")brandIdInt, _ := strconv.Atoi(brandId)request.Brand = int32(brandIdInt)r, err := global.GoodsSrvClient.GoodsList(context.Background(), request)if err != nil {zap.S().Errorw("[List] 查询 【商品列表】 失败")HandleGrpcErrorToHttp(err, ctx)return}reMap := map[string]interface{}{"total": r.Total,"data":  r.Data,}ctx.JSON(http.StatusOK, reMap)
}

但是呢,这种写法存在一定的问题,就是在http接口中未存在对于 后端数据的修正,这样子不便于前端与后端的数据对接,所以我们在 http 端一般可以做一个修改确定:

	reMap := map[string]interface{}{"total": r.Total,}// 这里一般会进行数据处理goodsList := make([]interface{}, 0)for _, value := range r.Data {goodsList = append(goodsList, map[string]interface{}{"id":          value.Id,"name":        value.Name,"goods_brief": value.GoodsBrief,"desc":        value.GoodsDesc,"ship_free":   value.ShipFree,"images":      value.Images,"desc_images": value.DescImages,"front_image": value.GoodsFrontImage,"shop_price":  value.ShopPrice,"category": map[string]interface{}{"id":   value.Category.Id,"name": value.Category.Name,},"brand": map[string]interface{}{"id":   value.Brand.Id,"name": value.Brand.Name,"logo": value.Brand.Logo,},"is_hot":  value.IsHot,"is_new":  value.IsNew,"on_sale": value.OnSale,})}reMap["data"] = goodsListctx.JSON(http.StatusOK, reMap)

注册中心内容抽取

有时候,我们的项目不是完全基于某一个注册中心,我们不希望将注册中心的逻辑集成在 main文件中,我们希望我们的项目具有快速替换性,这个时候就需要我们将注册中心的内容抽取出来

注册中心服务注册

我们在 util 包下创建一个 register 包,再在 register包下创建 consul包,再在 consul包下创建register.go:

package consulimport ("fmt""github.com/hashicorp/consul/api""mxshop-api/goods-web/global"
)// 重新配置Register,令其单独拎出来
// 注册类,这是一个类
type Registry struct {Host stringPort int
}// 这个是类的能力,能干什么
type RegistryClient interface {Register(address string, port int, name string, tags []string, id string) errorDeRegister(serviceId string) error
}func NewRegistryClient(host string, port int) RegistryClient {return &Registry{Host: host,Port: port,}
}func (r *Registry) Register(address string, port int, name string, tags []string, id string) error {cfg := api.DefaultConfig()cfg.Address = fmt.Sprintf("%s:%d", r.Host, r.Port) // consul 的地址client, err := api.NewClient(cfg)if err != nil {panic(err)}// 生成 consul 的注册对象// 配置基础信息registration := new(api.AgentServiceRegistration)registration.Name = nameregistration.ID = idregistration.Tags = tagsregistration.Port = portregistration.Address = address// 配置检查对象,也就是健康检查机制check := &api.AgentServiceCheck{HTTP:                           fmt.Sprintf("http://%s:%d/health", global.ServerConfig.Host, global.ServerConfig.Port), // 发送 GET 请求来进行健康检查,服务的地址Timeout:                        "5s",                                                                                   // 每次健康检查中,多久没有回复视为健康检查失败Interval:                       "5s",                                                                                   // 进行健康检查的频率DeregisterCriticalServiceAfter: "10s",                                                                                  // 不健康服务允许存活的时间,当一个服务被检查为不健康时,若 10s 内其没有转为健康,则将其从服务中删除}// 将检查对象配置进 consul 的注册对象 registration 中registration.Check = check// 将配置的 consul 注册进去err = client.Agent().ServiceRegister(registration)client.Agent().ServiceDeregister(name)if err != nil {panic(err)}return nil}func (r *Registry) DeRegister(serviceId string) error {return nil
}

在这个程序中,我们要注意的是程序的注册与鸭子类型的实际应用,同时理解golang的设计思想

main.go

	// 动态配置 Consul 相关信息register_client := consul.NewRegistryClient(global.ServerConfig.ConsulInfo.Host, global.ServerConfig.ConsulInfo.Port)serviceId := fmt.Sprintf("%s", uuid.NewV4())err := register_client.Register(global.ServerConfig.Host, global.ServerConfig.Port, global.ServerConfig.Name, global.ServerConfig.Tags, serviceId)if err != nil {zap.S().Panic("服务注册失败", err.Error())}

注册中心服务注销

优雅的利用go 的队列机制注销服务

	quit := make(chan os.Signal)// 如果接收到了 kill 或 ctrl C,则进入quitsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quitif err = register_client.DeRegister(serviceId); err != nil {zap.S().Panic("注销失败:", err.Error())} else {zap.S().Panic("注销成功")}

新建商品接口

在router中添加路由,这个商品需要管理员权限才可以新建

func InitGoodsRouter(Router *gin.RouterGroup) {// 这样就需要 /user/list 才可以进行访问了GoodsRouter := Router.Group("goods"){// 在这里添加拦截器的作用响应位置GoodsRouter.GET("", goods.List)GoodsRouter.POST("", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New)}
}

同时我们在 api/goods.go 中添加新建商品的接口

func New(ctx *gin.Context) {// 由于这里使用了 类似于 Vo 的感觉,所以先构建一个用来绑定获取到的前端数据goodsForm := forms.GoodsForm{}// 绑定 ShouldBind可以同时绑定form和json,这里就写的明确一点,视为json形式if err := ctx.ShouldBindJSON(&goodsForm); err != nil {HandleValidatorError(ctx, err)return}// 利用proto 从 grpc服务中获取所需的信息goodsClient := global.GoodsSrvClientrsp, 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,GoodsDesc:       goodsForm.GoodsDesc,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)
}

商品详情接口

在 router 中创建 单独商品详情接口:

GoodsRouter.GET("/:id", goods.Detail) // 捕捉类似于/goods/123 的内容中的 123
func Detail(ctx *gin.Context) {// 获取拼接在 链接之后的参数id := ctx.Param("id") // 获取 /goods/123i, err := strconv.ParseInt(id, 10, 32)if err != nil {ctx.Status(http.StatusNotFound)return}// 发送 grpc 请求查询相关内容r, err := global.GoodsSrvClient.GetGoodsDetail(context.Background(), &proto.GoodInfoRequest{Id: int32(i),})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)}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,"category": 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)
}

删除商品接口

在 router 中创建 删除商品的接口:

GoodsRouter.DELETE("/:id", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.Delete)
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 {api.HandleGrpcErrorToHttp(err, ctx)return}ctx.Status(http.StatusOK)return
}

获取商品库存接口

库存接口正在开发中

在router 中新增接口

GoodsRouter.GET("/:id/stocks", goods.Stocks) // 获取商品库存
func Stocks(ctx *gin.Context) {id := ctx.Param("id")_, err := strconv.ParseInt(id, 10, 32)if err != nil {ctx.Status(http.StatusNotFound)return}// TODO 商品库存相关信息
}

更新商品状态接口

GoodsRouter.PATCH("/:id", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.UpdateStatus)
func UpdateStatus(ctx *gin.Context) {id := ctx.Param("id")i, err := strconv.ParseInt(id, 10, 32)if err != nil {ctx.Status(http.StatusNotFound)return}goodsStatusForm := forms.GoodsStatusForm{}if err := ctx.ShouldBindJSON(&goodsStatusForm); err != nil {api.HandleValidatorError(ctx, err)return}if _, err = global.GoodsSrvClient.UpdateGoods(context.Background(), &proto.CreateGoodsInfo{Id:     int32(i),IsNew:  *goodsStatusForm.IsNew,IsHot:  *goodsStatusForm.IsHot,OnSale: *goodsStatusForm.OnSale,}); err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}ctx.JSON(http.StatusOK, gin.H{"msg": "修改成功",})
}

更新商品接口

GoodsRouter.PUT("/:id", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.Update)
func Update(ctx *gin.Context) {id := ctx.Param("id")i, err := strconv.ParseInt(id, 10, 32)if err != nil {ctx.Status(http.StatusNotFound)return}goodsForm := forms.GoodsForm{}if err = ctx.ShouldBindJSON(&goodsForm); err != nil {api.HandleValidatorError(ctx, err)return}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,GoodsDesc:       goodsForm.GoodsDesc,ShipFree:        *goodsForm.ShipFree,Images:          goodsForm.Images,DescImages:      goodsForm.DescImages,GoodsFrontImage: goodsForm.FrontImage,CategoryId:      goodsForm.CategoryId,BrandId:         goodsForm.Brand,}); err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}ctx.JSON(http.StatusOK, gin.H{"msg": "修改成功",})}

分类的相关接口

我们在做分类接口之前,意识到有三个接口是大家都共用的,分别是:removeTopStruct、HandleGrpcErrorToHttp、HandleValidatorError ,所以我们可以把这三个接口都抽到base.go 中:

base.go(在 api 包下创建的新接口)

package apiimport ("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""net/http""strings"
)func HandleGrpcErrorToHttp(err error, c *gin.Context) {// 将 grpc 的状态码转换为 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": "参数错误",})default:c.JSON(http.StatusInternalServerError, gin.H{"msg": "其他错误:" + e.Message(),})}}}
}// 在最后返回错误时调用,用来将返回中的对象名去掉
func RemoveTopStruct(fields map[string]string) map[string]string {rsp := map[string]string{}for field, err := range fields {rsp[field[strings.Index(field, ".")+1:]] = err // 将map中的 key 中的 . 前面的信息去掉}return rsp
}func HandleValidatorError(c *gin.Context, err error) {errs, ok := err.(validator.ValidationErrors)if !ok {c.JSON(http.StatusOK, gin.H{"msg": err.Error(),})}c.JSON(http.StatusBadRequest, gin.H{"msg": RemoveTopStruct(errs.Translate(global.Trans)),})return
}

创建所需表单

forms/category.go

package formstype CategoryForm struct {Name           string `form:"name" json:"name" binding:"required,min=3,max=20"`ParentCategory int32  `form:"parent" json:"parent"`Level          int32  `form:"level" json:"level" binding:"required,oneof=1 2 3"`IsTab          *bool  `form:"is_tab" json:"is_tab" binding:"required"`
}type UpdateCategoryForm struct {Name  string `form:"name" json:"name" binding:"required,min=3,max=20"`IsTab *bool  `form:"is_tab" json:"is_tab"`
}

创建接口

所有接口:

package categoryimport ("context""encoding/json""github.com/gin-gonic/gin""go.uber.org/zap""google.golang.org/protobuf/types/known/emptypb""mxshop-api/goods-web/api""mxshop-api/goods-web/forms""mxshop-api/goods-web/global""mxshop-api/goods-web/proto""net/http""strconv"
)func List(ctx *gin.Context) {r, err := global.GoodsSrvClient.GetAllCategorysList(context.Background(), &emptypb.Empty{})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}// 创建一个数组data := make([]interface{}, 0)err = json.Unmarshal([]byte(r.JsonData), &data) // 这里是逻辑写成这样了,所有的分级分类会以JSON的形式存储在r.JsonData 中,这里是对应的反解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.IdreMap["name"] = r.Info.NamereMap["level"] = r.Info.LevelreMap["parent_category"] = r.Info.ParentCategoryreMap["sub_category"] = subCategorysctx.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)}request := make(map[string]interface{})request["id"] = rsp.Idrequest["name"] = rsp.Namerequest["parent"] = rsp.ParentCategoryrequest["level"] = rsp.Levelrequest["is_tab"] = rsp.IsTabctx.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)
}

添加 router

router/category.go

package routerimport ("github.com/gin-gonic/gin""mxshop-api/goods-web/api/category"
)func InitCategoryRouter(Router *gin.RouterGroup) {CategoryRouter := Router.Group("category"){CategoryRouter.GET("", category.List)CategoryRouter.DELETE("/:id", category.Delete)CategoryRouter.GET("/:id", category.Detail)CategoryRouter.POST("", category.New)CategoryRouter.PUT("/:id", category.Update)}
}

在Init 中添加category

initialize/router.go:

	ApiGroup := Router.Group("/g/v1")router2.InitGoodsRouter(ApiGroup)router2.InitCategoryRouter(ApiGroup) // q添加router2.InitHealthCheckRouter(Router.Group(""))

轮播图接口

添加轮播图Form

创建文件 forms/banner.go

package formstype 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"`
}

编写API

创建文件 api/banner/banner.go

package bannersimport ("context""github.com/gin-gonic/gin""google.golang.org/protobuf/types/known/emptypb""mxshop-api/goods-web/api""mxshop-api/goods-web/forms""mxshop-api/goods-web/global""mxshop-api/goods-web/proto""net/http""strconv"
)func List(ctx *gin.Context) {rsp, err := global.GoodsSrvClient.BannerList(context.Background(), &emptypb.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.IdreMap["index"] = value.IndexreMap["image"] = value.ImagereMap["url"] = value.Urlresult = 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),Image: bannerForm.Image,Url:   bannerForm.Url,})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}response := make(map[string]interface{})response["id"] = rsp.Idresponse["index"] = rsp.Indexresponse["url"] = rsp.Urlresponse["image"] = rsp.Imagectx.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, "")
}

编写Router

创建文件 router/banner.go

package routerimport ("github.com/gin-gonic/gin""mxshop-api/goods-web/api/banners""mxshop-api/goods-web/middlewares"
)func InitBannerRouter(Router *gin.RouterGroup) {BannerRouter := Router.Group("banner"){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)}
}

将Router添加至InitRouter

	ApiGroup := Router.Group("/g/v1")router2.InitGoodsRouter(ApiGroup)router2.InitCategoryRouter(ApiGroup)router2.InitBannerRouter(ApiGroup)router2.InitHealthCheckRouter(Router.Group(""))return Router

品牌接口

添加品牌表单

package formstype 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"`
}

添加品牌API

注意这里的分页是不好的写法,正确的应该在service层中实现,所以这里不要参考,具体可以参考商品那里的接口

package brandsimport ("context""net/http""strconv""github.com/gin-gonic/gin""mxshop-api/goods-web/api""mxshop-api/goods-web/forms""mxshop-api/goods-web/global""mxshop-api/goods-web/proto"
)func BrandList(ctx *gin.Context) {pn := ctx.DefaultQuery("pn", "0")pnInt, _ := strconv.Atoi(pn)pSize := ctx.DefaultQuery("psize", "10")pSizeInt, _ := strconv.Atoi(pSize)rsp, err := global.GoodsSrvClient.BrandList(context.Background(), &proto.BrandFilterRequest{Pages:       int32(pnInt),PagePerNums: int32(pSizeInt),})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}result := make([]interface{}, 0)reMap := make(map[string]interface{})reMap["total"] = rsp.Totalfor _, value := range rsp.Data[pnInt : pnInt*pSizeInt+pSizeInt] {reMap := make(map[string]interface{})reMap["id"] = value.IdreMap["name"] = value.NamereMap["logo"] = value.Logoresult = append(result, reMap)}reMap["data"] = resultctx.JSON(http.StatusOK, reMap)
}func NewBrand(ctx *gin.Context) {brandForm := forms.BrandForm{}if err := ctx.ShouldBindJSON(&brandForm); err != nil {api.HandleValidatorError(ctx, err)return}rsp, err := global.GoodsSrvClient.CreateBrand(context.Background(), &proto.BrandRequest{Name: brandForm.Name,Logo: brandForm.Logo,})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}request := make(map[string]interface{})request["id"] = rsp.Idrequest["name"] = rsp.Namerequest["logo"] = rsp.Logoctx.JSON(http.StatusOK, request)
}func DeleteBrand(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.DeleteBrand(context.Background(), &proto.BrandRequest{Id: int32(i)})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}ctx.Status(http.StatusOK)
}func UpdateBrand(ctx *gin.Context) {brandForm := forms.BrandForm{}if err := ctx.ShouldBindJSON(&brandForm); 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.UpdateBrand(context.Background(), &proto.BrandRequest{Id:   int32(i),Name: brandForm.Name,Logo: brandForm.Logo,})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}ctx.Status(http.StatusOK)
}func GetCategoryBrandList(ctx *gin.Context) {id := ctx.Param("id")i, err := strconv.ParseInt(id, 10, 32)if err != nil {ctx.Status(http.StatusNotFound)return}rsp, err := global.GoodsSrvClient.GetCategoryBrandList(context.Background(), &proto.CategoryInfoRequest{Id: int32(i),})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.IdreMap["name"] = value.NamereMap["logo"] = value.Logoresult = append(result, reMap)}ctx.JSON(http.StatusOK, result)
}func CategoryBrandList(ctx *gin.Context) {//所有的list返回的数据结构/*{"total": 100,"data":[{},{}]}*/rsp, err := global.GoodsSrvClient.CategoryBrandList(context.Background(), &proto.CategoryBrandFilterRequest{})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}reMap := map[string]interface{}{"total": rsp.Total,}result := make([]interface{}, 0)for _, value := range rsp.Data {reMap := make(map[string]interface{})reMap["id"] = value.IdreMap["category"] = map[string]interface{}{"id":   value.Category.Id,"name": value.Category.Name,}reMap["brand"] = map[string]interface{}{"id":   value.Brand.Id,"name": value.Brand.Name,"logo": value.Brand.Logo,}result = append(result, reMap)}reMap["data"] = resultctx.JSON(http.StatusOK, reMap)
}func NewCategoryBrand(ctx *gin.Context) {categoryBrandForm := forms.CategoryBrandForm{}if err := ctx.ShouldBindJSON(&categoryBrandForm); err != nil {api.HandleValidatorError(ctx, err)return}rsp, err := global.GoodsSrvClient.CreateCategoryBrand(context.Background(), &proto.CategoryBrandRequest{CategoryId: int32(categoryBrandForm.CategoryId),BrandId:    int32(categoryBrandForm.BrandId),})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}response := make(map[string]interface{})response["id"] = rsp.Idctx.JSON(http.StatusOK, response)
}func UpdateCategoryBrand(ctx *gin.Context) {categoryBrandForm := forms.CategoryBrandForm{}if err := ctx.ShouldBindJSON(&categoryBrandForm); 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.UpdateCategoryBrand(context.Background(), &proto.CategoryBrandRequest{Id:         int32(i),CategoryId: int32(categoryBrandForm.CategoryId),BrandId:    int32(categoryBrandForm.BrandId),})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}ctx.Status(http.StatusOK)
}func DeleteCategoryBrand(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.DeleteCategoryBrand(context.Background(), &proto.CategoryBrandRequest{Id: int32(i)})if err != nil {api.HandleGrpcErrorToHttp(err, ctx)return}ctx.JSON(http.StatusOK, "")
}

添加品牌Router

package routerimport ("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)   //获取分类的品牌}
}

品牌路由导入路由表

	ApiGroup := Router.Group("/g/v1")router2.InitGoodsRouter(ApiGroup)router2.InitCategoryRouter(ApiGroup)router2.InitBannerRouter(ApiGroup)router2.InitBrandRouter(ApiGroup)router2.InitHealthCheckRouter(Router.Group(""))

http://www.ppmy.cn/ops/161880.html

相关文章

常用的css自用记录

1.选中父元素的input框聚焦 focus-within&#xff0c;普通的focus只适用于input框&#xff0c; focus-within给input框的父元素设置&#xff0c;可以设置聚焦时父元素的样式设置 2.常用于表单必填项星号设置 以前都是直接使用<span>*</span>然后设置样式&#xff…

DeepBI AI驱动的关键词出价优化策略:提升亚马逊广告ROI的关键

在亚马逊广告投放中&#xff0c;关键词的出价策略在提升广告曝光量、点击率和最终销售转化率中扮演着至关重要的角色。为了最大化广告的投资回报率&#xff08;ROI&#xff09;&#xff0c;精准地控制关键词的出价变得尤为重要。通过智能化的动态调整&#xff0c;确保广告在恰当…

3-2 WPS JS宏 工作簿的打开与保存(模板批量另存为工作)学习笔记

************************************************************************************************************** 点击进入 -我要自学网-国内领先的专业视频教程学习网站 *******************************************************************************************…

记一次高并发下导致的数据库死锁解决方案

数据库:mysql、引擎:InnoDB&#xff0c;隔离级别为可重复读&#xff08;repeatable-read&#xff09; 如果你只是想删除锁住的事务看这里 你需要提前知道 InnoDB行锁的原理是通过给索引上的索引项加锁来实现的。InnoDB 默认情况下&#xff0c;对于普通的查询语句&#xff08…

本科《IPMC微传感阵列封装和制备方法的研究 》开题报告

一、课题意义 1.理论意义 本课题的理论研究不仅局限于IPMC材料的基础性能分析&#xff0c;更致力于构建一个全面、系统的理论框架&#xff0c;以解释和预测IPMC材料在复杂环境下的行为特性。通过深入研究以nafi膜为代表的先进材料在IPMC制备中的应用&#xff0c;我们期望能够揭…

html css js网页制作成品——HTML+CSS蒧蒧面包店的网页设计(5页)附源码

目录 一、👨‍🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML

React进阶之前端业务Hooks库(四)

前端业务Hooks库 其他功能的hook针对domuseClickAwayuseDocumentVisibilityuseEventListeneruseMutationObserveruseResponsive结合组件库(ant design,element ui)其他功能的hook 针对dom 合理的使用useLatest,useMemoizedFn,能够保证组件的更新是不发生不必要的变化的。 后…

AWS API Gateway灰度验证实现

在微服务架构中,灰度发布(金丝雀发布)是验证新版本稳定性的核心手段。通过将小部分流量(如 10%)导向新版本服务,可以在不影响整体系统的情况下快速发现问题。AWS API Gateway 原生支持流量按比例分配功能,无需复杂编码即可实现灰度验证。本文将详细解析其实现方法、最佳…