【Go】十八、http 調用服務的編寫

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.pswp.cn/web/70795.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/70795.shtml
英文地址,請注明出處:http://en.pswp.cn/web/70795.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

WiseFlow本地搭建實錄---保姆教程

今天從零開始搭建了Wiseflow的本地環境搭建&#xff0c;目前使用的都是免費的API&#xff0c;我建議大家可以一起嘗試一下搭建自己的關鍵信息的數據庫&#xff0c;我是windows的環境&#xff0c;但是其他的應該也差不多&#xff0c;踩了很多坑&#xff0c;希望這篇文章能幫大家…

數的計算(藍橋云課)

題目描述 輸入一個自然數 n (n≤1000)n (n≤1000)&#xff0c;我們對此自然數按照如下方法進行處理: 不作任何處理; 在它的左邊加上一個自然數,但該自然數不能超過原數的一半; 加上數后,繼續按此規則進行處理,直到不能再加自然數為止。 問總共可以產生多少個數。 輸入描述 輸…

知識庫功能測試難點

圖表交互功能測試難點 知識庫圖表類型多&#xff0c;每種圖表交互功能不同。像柱狀圖&#xff0c;可能有點擊柱子查看詳細數據、鼠標懸停顯示數據提示等交互&#xff1b;折線圖除了這些&#xff0c;還可能支持縮放查看不同時間段數據。多種交互操作在不同圖表間存在差異&#x…

【人工智能】數據挖掘與應用題庫(201-300)

1、在LetNet5網絡中,卷積核的大小是? 答案:5*5 2、LeNet5網絡參數的數量約為? 答案:6萬 3、AlexNet與LeNet5相比,使用了哪些機制來改進模型的訓練過程? 答案: 數據增廣Dropout抑制過擬合ReLU激活函數CUDA加速神經網絡訓練4、VGGNet使用的卷積核的大小是? 答案:…

web安全滲透測試 APP安全滲透漏洞測試詳情

前言 小小白承包了一塊20畝的土地&#xff0c;依山傍水&#xff0c;風水不錯。聽朋友說去年玉米大賣&#xff0c;他也想嘗嘗甜頭&#xff0c;也就種上了玉米。 看著玉米茁壯成長&#xff0c;別提小小白心里多開心&#xff0c;心里盤算著玉米大買后&#xff0c;吃香喝辣的富貴…

CSS處理內容溢出

<!DOCTYPE html> <html lang"zh-cn"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>處理內容溢出</title><style>#d1{wid…

拉丁超立方采樣(Latin Hypercube Sampling)技術詳解及實現

拉丁超立方采樣(Latin Hypercube Sampling)技術詳解 拉丁超立方采樣(Latin Hypercube Sampling)技術詳解1. 引言2. 拉丁超立方采樣原理3. 數學公式描述4. Python代碼實現代碼解析5. 應用場景與優勢6. 在化工中的應用6.1 工藝參數優化6.2 不確定性量化與風險評估6.3 實驗設計…

docker-compose部署onlyoffice8.3.0并支持ssl,且支持通過nginx代理,關閉JWT配置

編寫docker-compose文件 mkdir -p /data/onlyoffice && echo "version: 3services:onlyoffice:container_name: OnlyOfficeimage: onlyoffice/documentserver:8.3.0restart: alwaysports:- 8088:80- 64431:443environment:TZ: Asia/ShanghaiJWT_ENABLED: falsevol…

Sliding Window Attention(滑動窗口注意力)解析: Pytorch實現并結合全局注意力(Global Attention )

Sliding Window Attention&#xff08;滑動窗口注意力&#xff09;解析 Sliding Window Attention&#xff08;滑動窗口注意力&#xff09; 是 Longformer (來源&#xff1a;https://arxiv.org/pdf/2004.05150)提出的 稀疏注意力機制&#xff0c;旨在解決 標準 Transformer 計算…

【運維】內網服務器借助通過某臺可上外網的服務器實現公網訪問

背景&#xff1a; 內網服務器無法連接公網,但是辦公電腦可以連接內網服務器又可以連接公網。 安裝軟件 1、frp 2、ccproxy 配置 1、內網服務器 # 內網服務器啟動frp服務配置文件參考vi frps.ini# frps.ini [common] bind_port 7000# 備注: bind_port端口可以隨意配置。配置完…

flask 是如何分發請求的?

這篇博客會涉及一些 WSGI 的知識&#xff0c;不了解的可以看這篇博客&#xff0c;簡單了解一下。 Python 的 WSGI 簡單入門 一、請求在 flask 中的處理過程 我們先來看一下 werkzeug.routing 包下 Map 和 Rule 方法的使用&#xff0c;這里給出一個官方的示例&#xff08;我進…

怎么獲取免費的 GPU 資源完成大語言模型(LLM)實驗

怎么獲取免費的 GPU 資源完成大語言模型(LLM)實驗 目錄 怎么獲取免費的 GPU 資源完成大語言模型(LLM)實驗在線平臺類Google ColabKaggle NotebooksHugging Face Spaces百度飛槳 AI Studio在線平臺類 Google Colab 特點:由 Google 提供的基于云端的 Jupyter 筆記本環境,提…

Python開發Django面試題及參考答案

目錄 Django 的請求生命周期是怎樣的? Django 的 MTV 架構中的各個組件分別是什么? Django 的 URL 路由是如何工作的? Django 的視圖函數和視圖類有什么區別? Django 的模板系統是如何渲染 HTML 的? Django 的 ORM 是如何工作的? Django 的中間件是什么?它的作用是…

【圖像的讀寫與基本操作】

圖像的讀寫與基本操作 目錄 圖像的讀寫與基本操作目標知識點1. 圖像的讀寫 &#xff1a;2. 圖像的縮放 &#xff1a;3. 圖像的翻轉 &#xff1a;4. 圖像的裁剪 &#xff1a;5. 顏色空間轉換 &#xff1a; 示例代碼1. 圖像的讀寫 &#xff1a;2. 圖像的縮放 &#xff1a;3. 圖像…

《數字圖像處理》筆記

文章目錄 第一章 緒論1.1 什么是數字圖像處理數字圖像的概念數字圖像的組成數字圖像處理的概念 1.4 數字圖像處理的基本步驟 第二章 數字圖像基礎2.2 光和電磁波譜可見光單色光灰度級發光強度光通量亮度 2.3 圖像感知和獲取將照射能量變換為數字圖像的傳感器簡單的圖像形成模型…

網絡安全掃描--基礎篇

前言 1、了解互聯網安全領域中日趨重要的掃描技術 2、了解在不同網絡場景下掃描技術手段 3、熟悉linux下系統內核防護策略并能大件一個有效的系統防護體系 4、增強工作安全意識&#xff0c;并能有效的實踐于工作場景中 目錄 1、熟悉主機掃描工具&#xff08;fping&#xff0c;…

前端防重復請求終極方案:從Loading地獄到精準攔截的架構升級

&#x1f525; 事故現場還原&#xff1a;瘋狂點擊引發的血案 凌晨1點23分&#xff0c;監控系統突然告警&#xff1a; &#x1f4c9; 服務器CPU飆升至98% &#x1f5c3;? 數據庫出現3000臟數據 &#x1f4a5; 用戶端彈出上百個錯誤彈窗 事故原因&#xff1a;黑產腳本通過0.5秒…

基于Spring Boot的供應商管理系統設計與實現(LW+源碼+講解)

專注于大學生項目實戰開發,講解,畢業答疑輔導&#xff0c;歡迎高校老師/同行前輩交流合作?。 技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;…

Redis|事務

文章目錄 是什么能干嘛Redis 事務 VS 數據庫事務怎么玩小總結 是什么 首先回想一下什么是數據庫的事務&#xff1f;數據庫事務是指作為單個邏輯單元執行的一系列操作&#xff0c;具備以下四個關鍵特性&#xff08;ACID&#xff09;&#xff1a; 原子性&#xff08;Atomicity&am…

一周學會Flask3 Python Web開發-Jinja2模板繼承和include標簽使用

鋒哥原創的Flask3 Python Web開發 Flask3視頻教程&#xff1a; 2025版 Flask3 Python web開發 視頻教程(無廢話版) 玩命更新中~_嗶哩嗶哩_bilibili 不管是開發網站還是后臺管理系統&#xff0c;我們頁面里多多少少有公共的模塊。比如博客網站&#xff0c;就有公共的頭部&…