当前位置:网站首页>20、商品微服务-web层实现
20、商品微服务-web层实现
2022-08-03 00:35:00 【无休止符】
目录
一、快速新建goods_web服务
1 - 新建项目goods_web
- 参考之前的user_web服务:
- 将项目拷贝过来,然后将不需要的删除,也可以直接跳过,下载之后的完整源码;以下介绍一些关键的修改
- 使用目录的方式替换import:将
user_web
替换为goods_web
- goods_web/api/goods/api_goods.go:新增一个List实现,不需要其他逻辑
- goods_web/initialize/init_router.go
- nacos的namespace修改:这里以goods_web/config_zsz.yaml为例,另外2个自行修改
host: '192.168.124.51'
port: 8848
namespace: 'b2f00e3b-4e22-4e87-8129-9c02b5f1220d'
user: 'nacos'
password: 'nacos'
dataid: 'goods_web.json'
group: 'zsz'
- 克隆nacos配置:克隆user_web.json配置为goods_web.json,注意修改目标空间、DataId、Group
{
"name": "goods_web",
"port": 8082,
"goods_srv": {
"name": "goods_srv"
},
"jwt": {
"key": "VYLDYq3&hGWjWqF$K1ih"
},
"consul": {
"host": "192.168.124.51",
"port": 8500
}
}
2 - 快速测试
- 启动商品web服务:YApi测试200成功
- YApi新增商品测试接口
二、商品列表接口
- goods_web/api/goods/api_goods.go:实现List
func List(ctx *gin.Context) {
fmt.Println("商品列表")
//商品的列表
根据前端的请求参数构造request对象
request := &proto.GoodsFilterRequest{
}
priceMin := ctx.DefaultQuery("pmin", "0")
priceMinInt, _ := 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 = keywords
brandId := ctx.DefaultQuery("b", "0")
brandIdInt, _ := strconv.Atoi(brandId)
request.Brand = int32(brandIdInt)
请求商品的service服务
r, err := global.GoodsSrvClient.GoodsList(context.WithValue(context.Background(), "ginContext", ctx), request)
if err != nil {
zap.S().Errorw("[List] 查询 【商品列表】失败")
HandleGrpcErrorToHttp(err, ctx)
return
}
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"] = goodsList
ctx.JSON(http.StatusOK, reMap)
}
- YApi设置:
三、goods_web服务注册与注销
- 需求分析:提高注册中心的扩展性,比如之后不想用consul,想换nacos、etcd等
- nacos配置:
{
"host": "192.168.78.1",
"name": "goods_web",
"port": 8082,
"tags": ["mxshop","imooc","bobby","goods","web"],
"goods_srv": {
"name": "goods_srv"
},
"jwt": {
"key": "VYLDYq3&hGWjWqF$K1ih"
},
"consul": {
"host": "192.168.78.131",
"port": 8500
}
}
- goods_web/config/config.go:ServerConfig添加本机的host和Tags
type ServerConfig struct {
Name string `mapstructure:"name" json:"name"`
Host string `mapstructure:"host" json:"host"`
Tags []string `mapstructure:"tags" json:"tags"`
Port int `mapstructure:"port" json:"port"`
GoodsSrvInfo GoodsSrvConfig `mapstructure:"goods_srv" json:"goods_srv"`
JWTInfo JWTConfig `mapstructure:"jwt" json:"jwt"`
ConsulInfo ConsulConfig `mapstructure:"consul" json:"consul"`
}
- goods_web/utils/register/consul/reg_consul.go:封装consul的注册与注销
package consul
import (
"fmt"
"github.com/hashicorp/consul/api"
)
type Registry struct {
Host string
Port int
}
type RegistryClient interface {
Register(address string, port int, name string, tags []string, id string) error
DeRegister(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)
client, err := api.NewClient(cfg)
if err != nil {
panic(err)
}
//生成对应的检查对象
check := &api.AgentServiceCheck{
HTTP: fmt.Sprintf("http://%s:%d/health", address, port),
Timeout: "5s",
Interval: "5s",
DeregisterCriticalServiceAfter: "10s",
}
//生成注册对象
registration := new(api.AgentServiceRegistration)
registration.Name = name
registration.ID = id
registration.Port = port
registration.Tags = tags
registration.Address = address
registration.Check = check
err = client.Agent().ServiceRegister(registration)
if err != nil {
panic(err)
}
return nil
}
func (r *Registry) DeRegister(serviceId string) error {
cfg := api.DefaultConfig()
cfg.Address = fmt.Sprintf("%s:%d", r.Host, r.Port)
client, err := api.NewClient(cfg)
if err != nil {
return err
}
err = client.Agent().ServiceDeregister(serviceId)
return err
}
- goods_web/main.go
package main
import (
"fmt"
"github.com/satori/go.uuid"
"github.com/spf13/viper"
"go.uber.org/zap"
"os"
"os/signal"
"syscall"
"web_api/goods_web/global"
"web_api/goods_web/initialize"
"web_api/goods_web/utils"
"web_api/goods_web/utils/register/consul"
)
func main() {
//1. 初始化logger
initialize.InitLogger()
//2. 初始化配置文件
initialize.InitConfig()
//3. 初始化routers
Router := initialize.Routers()
//4. 初始化翻译
if err := initialize.InitTrans("zh"); err != nil {
panic(err)
}
//5. 初始化srv的连接
initialize.InitSrvConn()
viper.AutomaticEnv()
//如果是本地开发环境端口号固定,线上环境启动获取端口号
flag := viper.GetInt("DEV_CONFIG")
if flag > 4 {
// 1=zsz 2=comp 3=home
port, err := utils.GetFreePort()
if err == nil {
global.ServerConfig.Port = port
}
}
registerClient := consul.NewRegistryClient(global.ServerConfig.ConsulInfo.Host, global.ServerConfig.ConsulInfo.Port)
serviceId := fmt.Sprintf("%s", uuid.NewV4())
err := registerClient.Register(global.ServerConfig.Host, global.ServerConfig.Port, global.ServerConfig.Name, global.ServerConfig.Tags, serviceId)
if err != nil {
zap.S().Panic("服务注册失败:", err.Error())
}
zap.S().Debugf("启动服务器, 端口: %d", global.ServerConfig.Port)
go func() {
if err := Router.Run(fmt.Sprintf(":%d", global.ServerConfig.Port)); err != nil {
zap.S().Panic("启动失败:", err.Error())
}
}()
//接收终止信号
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
if err = registerClient.DeRegister(serviceId); err != nil {
zap.S().Info("注销失败:", err.Error())
} else {
zap.S().Info("注销成功:")
}
}
四、user_web服务注册与注销
- user_web/config/config.go
type ServerConfig struct {
Name string `mapstructure:"name" json:"name"`
Host string `mapstructure:"host" json:"host"`
Tags []string `mapstructure:"tags" json:"tags"`
Port int `mapstructure:"port" json:"port"`
UserSrvInfo UserSrvConfig `mapstructure:"user_srv" json:"user_srv"`
JWTInfo JWTConfig `mapstructure:"jwt" json:"jwt"`
AliSmsInfo AliSmsConfig `mapstructure:"sms" json:"sms"`
RedisInfo RedisConfig `mapstructure:"redis" json:"redis"`
ConsulInfo ConsulConfig `mapstructure:"consul" json:"consul"`
}
- user_web/utils/register/consul/reg_consul.go
package consul
import (
"fmt"
"github.com/hashicorp/consul/api"
)
type Registry struct {
Host string
Port int
}
type RegistryClient interface {
Register(address string, port int, name string, tags []string, id string) error
DeRegister(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)
client, err := api.NewClient(cfg)
if err != nil {
panic(err)
}
//生成对应的检查对象
check := &api.AgentServiceCheck{
HTTP: fmt.Sprintf("http://%s:%d/health", address, port),
Timeout: "5s",
Interval: "5s",
DeregisterCriticalServiceAfter: "10s",
}
//生成注册对象
registration := new(api.AgentServiceRegistration)
registration.Name = name
registration.ID = id
registration.Port = port
registration.Tags = tags
registration.Address = address
registration.Check = check
err = client.Agent().ServiceRegister(registration)
if err != nil {
panic(err)
}
return nil
}
func (r *Registry) DeRegister(serviceId string) error {
cfg := api.DefaultConfig()
cfg.Address = fmt.Sprintf("%s:%d", r.Host, r.Port)
client, err := api.NewClient(cfg)
if err != nil {
return err
}
err = client.Agent().ServiceDeregister(serviceId)
return err
}
- user_web/main.go
package main
import (
"fmt"
uuid "github.com/satori/go.uuid"
"github.com/spf13/viper"
"os"
"os/signal"
"syscall"
"web_api/user_web/global"
"web_api/user_web/initialize"
"web_api/user_web/utils"
"web_api/user_web/utils/register/consul"
"github.com/gin-gonic/gin/binding"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"go.uber.org/zap"
myvalidator "web_api/user_web/validator"
)
func main() {
//1. 初始化logger
initialize.InitLogger()
//2. 初始化配置文件
initialize.InitConfig()
//3. 初始化routers
Router := initialize.Routers()
//4. 初始化翻译
if err := initialize.InitTrans("zh"); err != nil {
panic(err)
}
//5. 初始化srv的连接
initialize.InitSrvConn()
viper.AutomaticEnv()
//如果是本地开发环境端口号固定,线上环境启动获取端口号
flag := viper.GetInt("DEV_CONFIG")
if flag > 4 {
// 1=zsz 2=comp 3=home
port, err := utils.GetFreePort()
if err == nil {
global.ServerConfig.Port = port
}
}
//注册验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
_ = v.RegisterValidation("mobile", myvalidator.ValidateMobile)
_ = v.RegisterTranslation("mobile", global.Trans, func(ut ut.Translator) error {
return ut.Add("mobile", "{0} 非法的手机号码!", true) // see universal-translator for details
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("mobile", fe.Field())
return t
})
}
//服务注册
registerClient := consul.NewRegistryClient(global.ServerConfig.ConsulInfo.Host, global.ServerConfig.ConsulInfo.Port)
serviceId := fmt.Sprintf("%s", uuid.NewV4())
err := registerClient.Register(global.ServerConfig.Host, global.ServerConfig.Port, global.ServerConfig.Name, global.ServerConfig.Tags, serviceId)
if err != nil {
zap.S().Panic("服务注册失败:", err.Error())
}
/* 1. S()可以获取一个全局的sugar,可以让我们自己设置一个全局的logger 2. 日志是分级别的,debug, info , warn, error, fetal debug最低,fetal最高,如果配置成info,所有比info低的都不会输出 NewProduction默认日志级别为info NewDevelopment默认日志级别为debug 3. S函数和L函数很有用, 提供了一个全局的安全访问logger的途径 */
zap.S().Debugf("启动服务器, 端口: %d", global.ServerConfig.Port)
if err := Router.Run(fmt.Sprintf(":%d", global.ServerConfig.Port)); err != nil {
zap.S().Panic("启动失败:", err.Error())
}
//接收终止信号
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
if err = registerClient.DeRegister(serviceId); err != nil {
zap.S().Info("注销失败:", err.Error())
} else {
zap.S().Info("注销成功:")
}
}
五、goods_api接口实现
1 - 添加商品接口
- goods_web/forms/form_goods.go
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"`
}
- goods_web/router/router_goods.go
package router
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/api/goods"
)
func InitGoodsRouter(Router *gin.RouterGroup) {
GoodsRouter := Router.Group("goods")
{
GoodsRouter.GET("", goods.List) //商品列表
//GoodsRouter.POST("", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New) //该接口需要管理员权限
GoodsRouter.POST("", goods.New) // 我们测试先将这2个接口权限关闭掉
}
}
- goods_web/api/goods/api_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)
}
- YApi
2 - 获取商品详情
- goods_web/router/router_goods.go:添加路由
package router
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/api/goods"
)
func InitGoodsRouter(Router *gin.RouterGroup) {
GoodsRouter := Router.Group("goods")
{
GoodsRouter.GET("", goods.List) //商品列表
//GoodsRouter.POST("", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New) //该接口需要管理员权限
GoodsRouter.POST("", goods.New) // 我们测试先将这2个接口权限关闭掉
GoodsRouter.GET("/:id", goods.Detail) //获取商品的详情
}
}
- goods_web/api/goods/api_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)
}
3 - 商品删除、更新
- goods_web/router/router_goods.go:添加路由
package router
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/api/goods"
)
func InitGoodsRouter(Router *gin.RouterGroup) {
GoodsRouter := Router.Group("goods")
{
GoodsRouter.GET("", goods.List) //商品列表
//GoodsRouter.POST("", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New) //该接口需要管理员权限
GoodsRouter.POST("", goods.New) // 我们测试先将这2个接口权限关闭掉
GoodsRouter.GET("/:id", goods.Detail) //获取商品的详情
//GoodsRouter.DELETE("/:id",middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.Delete) //删除商品
GoodsRouter.DELETE("/:id", goods.Delete) // 我们测试先将这2个接口权限关闭掉
GoodsRouter.GET("/:id/stocks", goods.Stocks) //获取商品的库存
//GoodsRouter.PATCH("/:id",middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.UpdateStatus)
GoodsRouter.PATCH("/:id", goods.UpdateStatus)
}
}
- goods_web/forms/form_goods.go:
package router
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/api/goods"
)
func InitGoodsRouter(Router *gin.RouterGroup) {
GoodsRouter := Router.Group("goods")
{
GoodsRouter.GET("", goods.List) //商品列表
//GoodsRouter.POST("", middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.New) //该接口需要管理员权限
GoodsRouter.POST("", goods.New) // 我们测试先将这2个接口权限关闭掉
GoodsRouter.GET("/:id", goods.Detail) //获取商品的详情
//GoodsRouter.DELETE("/:id",middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.Delete) //删除商品
GoodsRouter.DELETE("/:id", goods.Delete) // 我们测试先将这2个接口权限关闭掉
GoodsRouter.GET("/:id/stocks", goods.Stocks) //获取商品的库存
//GoodsRouter.PUT("/:id",middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.Update)
GoodsRouter.PUT("/:id", goods.Update)
//GoodsRouter.PATCH("/:id",middlewares.JWTAuth(), middlewares.IsAdminAuth(), goods.UpdateStatus)
GoodsRouter.PATCH("/:id", goods.UpdateStatus)
}
}
- goods_web/api/goods/api_goods.go
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 Stocks(ctx *gin.Context) {
id := ctx.Param("id")
_, err := strconv.ParseInt(id, 10, 32)
if err != nil {
ctx.Status(http.StatusNotFound)
return
}
//TODO 商品的库存
return
}
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.IsHot,
IsNew: *goodsStatusForm.IsNew,
OnSale: *goodsStatusForm.OnSale,
}); err != nil {
HandleGrpcErrorToHttp(err, ctx)
return
}
ctx.JSON(http.StatusOK, gin.H{
"msg": "修改成功",
})
}
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": "更新成功",
})
}
4 - 商品分类接口
- goods_web/router/router_category.go
package router
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/api/category"
)
func InitCategoryRouter(Router *gin.RouterGroup) {
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) //修改分类信息
}
}
- goods_web/forms/form_category.go
package forms
type 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"`
}
- goods_web/api/category/category.go:代码太多,就贴下方法图片,具体查看完整源码
这里总结下套路:
1、先添加form表单的struct
2、实现API方法
3、添加router
5 - 轮播图接口
- goods_web/forms/form_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"`
}
- goods_web/api/banners/api_banner.go
- goods_web/router/router_banner.go
package router
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/api/banners"
)
func InitBannerRouter(Router *gin.RouterGroup) {
BannerRouter := Router.Group("banners")
{
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) //修改轮播图信息
BannerRouter.DELETE("/:id", banners.Delete) // 删除轮播图
BannerRouter.POST("", banners.New) //新建轮播图
BannerRouter.PUT("/:id", banners.Update) //修改轮播图信息
}
}
6 - 品牌接口
- goods_web/api/brands/api_brand.go
- goods_web/forms/form_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"`
}
- goods_web/router/router_brand.go
package router
import (
"github.com/gin-gonic/gin"
"web_api/goods_web/api/brands"
)
// InitBrandRouter
//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) //获取分类的品牌
}
}
六、完整源码
- 完整源码下载:mxshop_srvsV8.3.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配置集导入文件
附:YApi快速添加接口方法
- 这里以轮播图举例
- 一开始我们还没有轮播图的数据,这里有4个接口
- 一开始我们还没有轮播图的数据,这里有4个接口
- 我们可以先实现添加轮播图的接口
- 反复编辑 -> 运行几次:数据库数据就有了
- 这时候我们再查询轮播图列表数据就有了
- 将返回的body中的json数据复制下,编辑接口-轮播图列表
- 在返回数据设置这选择导入,粘贴复制的json数据,就会自动帮我们生成返回数据的json
- 而我们需要做的仅仅就是修改下备注就可以了
边栏推荐
猜你喜欢
随机推荐
如何修复 SAP UI5 aggregation with cardinality 0..1 相关的错误消息
Visual Studio中vim模拟器
淘宝商品销量接口/淘宝商品销量监控接口/商品累计销量接口代码对接分享
TensorFlow学习记录(一):基本介绍
接口流量突增,如何做好性能优化?
【mysql知识点整理】--- order by 、group by 出现Using filesort原因详解
Jmeter二次开发实现rsa加密
写一个简单的网站步骤
v-if条件判断及v-show
买了一瓶饮料
聊聊 Nacos
【遥控器开发基础教程4】疯壳·开源编队无人机-SPI(OLED)
dataBinding的import导入
暴力递归到动态规划 08(小马走象棋)
10. SAP ABAP OData 服务如何支持修改(Update)操作
C语言:链表
图文详细解决IDEA使用Debug模式启动项目一直转圈圈跑起不来(亲测可以)
Moco of Mock tools use tutorial
封装和练习题目
一套开源的可快速搭建自己的物联网/智能家居系统源码