30、ES集成到项目中

一、go语言中使用ES

1 - 使用第三方库

package main

import (
	"encoding/json"
	"fmt"
	"github.com/olivere/elastic/v7"
)

func main() {
	//初始化一个连接
	host := "http://192.168.124.51:9200"
	// 这里必须将sniff设置为false,因为使用olivere/elastic连接elasticsearch时,发现连接地址命名输入的时候是地址
	// 但是连接时会自动转换成内网地址或者docker中的ip地址,导致服务连接不上
	_, err := elastic.NewClient(elastic.SetURL(host), elastic.SetSniff(false))
	if err != nil {
		panic(err)
	}
	q := elastic.NewMatchQuery("address", "street")
	src, err := q.Source()
	if err != nil {
		panic(err)
	}
	data, err := json.Marshal(src)
	got := string(data)
	fmt.Println(got)
}

在这里插入图片描述

2 - 解析出查询结果

package main

import (
	"context"
	"fmt"
	"github.com/olivere/elastic/v7"
)

func main() {
	//初始化一个连接
	host := "http://192.168.124.51:9200"
	// 这里必须将sniff设置为false,因为使用olivere/elastic连接elasticsearch时,发现连接地址命名输入的时候是地址
	// 但是连接时会自动转换成内网地址或者docker中的ip地址,导致服务连接不上
	client, err := elastic.NewClient(elastic.SetURL(host), elastic.SetSniff(false))
	if err != nil {
		panic(err)
	}
	q := elastic.NewMatchQuery("address", "street")
	result, err := client.Search().Index("user").Query(q).Do(context.Background())
	if err != nil {
		panic(err)
	}
	total := result.Hits.TotalHits.Value
	fmt.Printf("搜索结果数量:%d\n", total)
	for _, value := range result.Hits.Hits {
		if jsonData, err := value.Source.MarshalJSON(); err == nil {
			fmt.Println(string(jsonData))
		} else {
			panic(err)
		}
	}
}

在这里插入图片描述

3 - es对象转换为struct

  • 定义struct并使用go自带的json进行转换
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/olivere/elastic/v7"
)

type Account struct {
	AccountNum int32  `json:"account_number"`
	FirstName  string `json:"firstname"`
}

func main() {
	//初始化一个连接
	host := "http://192.168.124.51:9200"
	// 这里必须将sniff设置为false,因为使用olivere/elastic连接elasticsearch时,发现连接地址命名输入的时候是地址
	// 但是连接时会自动转换成内网地址或者docker中的ip地址,导致服务连接不上
	client, err := elastic.NewClient(elastic.SetURL(host), elastic.SetSniff(false))
	if err != nil {
		panic(err)
	}
	q := elastic.NewMatchQuery("address", "street")
	result, err := client.Search().Index("user").Query(q).Do(context.Background())
	if err != nil {
		panic(err)
	}
	total := result.Hits.TotalHits.Value
	fmt.Printf("搜索结果数量:%d\n", total)
	for _, value := range result.Hits.Hits {
		account := Account{}
		_ = json.Unmarshal(value.Source, &account)
		fmt.Println(account)
	}
}

在这里插入图片描述

4 - 向es中添加数据

  • 添加数据同时开启es的日志打印
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/olivere/elastic/v7"
	"log"
	"os"
)

type Account struct {
	AccountNum int32  `json:"account_number"`
	FirstName  string `json:"firstname"`
}

func main() {
	//初始化一个连接
	host := "http://192.168.124.51:9200"
	logger := log.New(os.Stdout, "mxshop", log.LstdFlags)
	// 这里必须将sniff设置为false,因为使用olivere/elastic连接elasticsearch时,发现连接地址命名输入的时候是地址
	// 但是连接时会自动转换成内网地址或者docker中的ip地址,导致服务连接不上
	client, err := elastic.NewClient(elastic.SetURL(host), elastic.SetSniff(false),
		elastic.SetTraceLog(logger))
	if err != nil {
		panic(err)
	}
	q := elastic.NewMatchQuery("address", "street")
	result, err := client.Search().Index("user").Query(q).Do(context.Background())
	if err != nil {
		panic(err)
	}
	total := result.Hits.TotalHits.Value
	fmt.Printf("搜索结果数量:%d\n", total)
	for _, value := range result.Hits.Hits {
		account := Account{}
		_ = json.Unmarshal(value.Source, &account)
		fmt.Println(account)
	}

	account := Account{AccountNum: 15468, FirstName: "immooc bobby"}
	put1, err := client.Index().Index("myuser").BodyJson(account).Do(context.Background())
	if err != nil {
		panic(err)
	}
	fmt.Printf("Indexed myuser %s to index %s, type %s \n", put1.Id, put1.Index, put1.Type)
}

在这里插入图片描述

5 - 新建mapping

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/olivere/elastic/v7"
	"log"
	"os"
)

const goodsMapping = `{
	"mappings" : {
		"properties" : {
			"name" : {
				"type" : "text",
				"analyzer":"ik_max_word"
			},
			"id" : {
				"type" : "integer"
			}
		}
	}
}`

type Account struct {
	AccountNum int32  `json:"account_number"`
	FirstName  string `json:"firstname"`
}

func main() {
	//初始化一个连接
	host := "http://192.168.124.51:9200"
	logger := log.New(os.Stdout, "mxshop", log.LstdFlags)
	// 这里必须将sniff设置为false,因为使用olivere/elastic连接elasticsearch时,发现连接地址命名输入的时候是地址
	// 但是连接时会自动转换成内网地址或者docker中的ip地址,导致服务连接不上
	client, err := elastic.NewClient(elastic.SetURL(host), elastic.SetSniff(false),
		elastic.SetTraceLog(logger))
	if err != nil {
		panic(err)
	}
	q := elastic.NewMatchQuery("address", "street")
	result, err := client.Search().Index("user").Query(q).Do(context.Background())
	if err != nil {
		panic(err)
	}
	total := result.Hits.TotalHits.Value
	fmt.Printf("搜索结果数量:%d\n", total)
	for _, value := range result.Hits.Hits {
		account := Account{}
		_ = json.Unmarshal(value.Source, &account)
		fmt.Println(account)
	}

	//account := Account{AccountNum: 15468, FirstName: "immooc bobby"}
	//put1, err := client.Index().Index("myuser").BodyJson(account).Do(context.Background())
	//if err != nil {
	//	panic(err)
	//}
	//fmt.Printf("Indexed myuser %s to index %s, type %s \n", put1.Id, put1.Index, put1.Type)

	createIndex, err := client.CreateIndex("mygoods").BodyString(goodsMapping).Do(context.Background())
	if err != nil {
		panic(err)
	}
	if !createIndex.Acknowledged {

	}
}

在这里插入图片描述
在这里插入图片描述


二、项目中集成ES

1 - 集成es接口分析

  • 对于商品操作来说:需要同步操作es的接口
    • 搜索、添加、更新、删除:这些都需要将数据同步到es的数据中
  • 将es的集成在srv层还是web层:应该将es集成到srv层,因为有可能出现mysql保存成功了,但是es保存失败了,这时候如果是在web层无法完成数据的回滚,这样就省去了微服务的事务问题
  • 使用es的目的:主要目的是搜索出商品的id来,通过id拿到具体的字段信息是通过mysql来完成的
  • 是否需要将所有的mysql字段在es中保存一份:实际开发中,我们一般只把搜索和用来过滤的字段信息保存到es中
  • es也可以来当做mysql使用
    • 但是实际上mysql和es之间是互补的关系;
    • mysql是关系数据库,es是文档数据库,各有各的优缺点;
    • 一般mysql用来做存储使用,es用来做搜索使用
  • es的性能提升
    • 想要提升es的性能,需要将es的内存设置的够大;
    • 但是实际上es可以设置的内存也是有上限的,所以我们不在es中保存不必要的字段,可以提高es的内存使用率,同样的内存可以载入的文档数量就会多一些,从另外的角度上来说也可以提高es的性能

2 - 建立商品对应的struct和mapping

  • goods_srv/model/es_goods.go
package model

type EsGoods struct {
	ID         int32 `json:"id"`
	CategoryID int32 `json:"category_id"`
	BrandsID   int32 `json:"brands_id"`
	OnSale     bool  `json:"on_sale"`
	ShipFree   bool  `json:"ship_free"`
	IsNew      bool  `json:"is_new"`
	IsHot      bool  `json:"is_hot"`

	Name        string  `json:"name"`
	ClickNum    int32   `json:"click_num"`
	SoldNum     int32   `json:"sold_num"`
	FavNum      int32   `json:"fav_num"`
	MarketPrice float32 `json:"market_price"`
	GoodsBrief  string  `json:"goods_brief"`
	ShopPrice   float32 `json:"shop_price"`
}

func (EsGoods) GetIndexName() string {
	return "goods"
}

func (EsGoods) GetMapping() string {
	goodsMapping := `
	{
		"mappings" : {
			"properties" : {
				"brands_id" : {
					"type" : "integer"
				},
				"category_id" : {
					"type" : "integer"
				},
				"click_num" : {
					"type" : "integer"
				},
				"fav_num" : {
					"type" : "integer"
				},
				"id" : {
					"type" : "integer"
				},
				"is_hot" : {
					"type" : "boolean"
				},
				"is_new" : {
					"type" : "boolean"
				},
				"market_price" : {
					"type" : "float"
				},
				"name" : {
					"type" : "text",
					"analyzer":"ik_max_word"
				},
				"goods_brief" : {
					"type" : "text",
					"analyzer":"ik_max_word"
				},
				"on_sale" : {
					"type" : "boolean"
				},
				"ship_free" : {
					"type" : "boolean"
				},
				"shop_price" : {
					"type" : "float"
				},
				"sold_num" : {
					"type" : "long"
				}
			}
		}
	}`
	return goodsMapping
}

3 - nacos新增es配置

{
  "name": "goods_srv",
  "host": "192.168.124.9",
  "tags": ["imooc", "bobby", "goods", "srv"],
  "mysql": {
    "host": "192.168.124.51",
    "port": 3306,
    "user": "root",
    "password": "jiushi",
    "db": "mxshop_goods_srv"
  },
  "consul": {
    "host": "192.168.124.51",
    "port": 8500
  },
  "es": {
    "host": "192.168.124.51",
    "port": 9200
  }
}
  • goods_srv/config/config.go:新增EsConfig结构体
package config

type MysqlConfig struct {
	Host     string `mapstructure:"host" json:"host"`
	Port     int    `mapstructure:"port" json:"port"`
	Name     string `mapstructure:"db" json:"db"`
	User     string `mapstructure:"user" json:"user"`
	Password string `mapstructure:"password" json:"password"`
}

type ConsulConfig struct {
	Host string `mapstructure:"host" json:"host"`
	Port int    `mapstructure:"port" json:"port"`
}

type EsConfig struct {
	Host string `mapstructure:"host" json:"host"`
	Port int    `mapstructure:"port" json:"port"`
}

type ServerConfig struct {
	Name       string       `mapstructure:"name" json:"name"`
	Host       string       `mapstructure:"host" json:"host"`
	Tags       []string     `mapstructure:"tags" json:"tags"`
	MysqlInfo  MysqlConfig  `mapstructure:"mysql" json:"mysql"`
	ConsulInfo ConsulConfig `mapstructure:"consul" json:"consul"`
	EsInfo     EsConfig     `mapstructure:"es" json:"es"`
}

type NacosConfig struct {
	Host      string `mapstructure:"host"`
	Port      uint64 `mapstructure:"port"`
	Namespace string `mapstructure:"namespace"`
	User      string `mapstructure:"user"`
	Password  string `mapstructure:"password"`
	DataId    string `mapstructure:"dataid"`
	Group     string `mapstructure:"group"`
}

  • goods_srv/global/global.go:添加EsClient对象
package global

import (
	"github.com/olivere/elastic/v7"
	"gorm.io/gorm"
	"nd/goods_srv/config"
)

var (
	DB           *gorm.DB
	ServerConfig config.ServerConfig
	NacosConfig  config.NacosConfig
	EsClient     *elastic.Client
)

4 - 初始化es

  • goods_srv/initialize/init_es.go
package initialize

import (
	"context"
	"fmt"
	"github.com/olivere/elastic/v7"
	"log"
	"nd/goods_srv/global"
	"nd/goods_srv/model"
	"os"
)

func InitEs() {
	//初始化连接
	host := fmt.Sprintf("http://%s:%d", global.ServerConfig.EsInfo.Host, global.ServerConfig.EsInfo.Port)
	logger := log.New(os.Stdout, "mxshop", log.LstdFlags)
	var err error
	global.EsClient, err = elastic.NewClient(elastic.SetURL(host), elastic.SetSniff(false),
		elastic.SetTraceLog(logger))
	if err != nil {
		panic(err)
	}

	//新建mapping和index
	exists, err := global.EsClient.IndexExists(model.EsGoods{}.GetIndexName()).Do(context.Background())
	if err != nil {
		panic(err)
	}
	if !exists { // 不存在的时候才需要新建mapping
		_, err = global.EsClient.CreateIndex(model.EsGoods{}.GetIndexName()).BodyString(model.EsGoods{}.GetMapping()).Do(context.Background())
		if err != nil {
			panic(err)
		}
	}
}

  • goods_srv/main.go:添加es的初始化调用
func main() {
	IP := flag.String("ip", "0.0.0.0", "ip地址")
	Port := flag.Int("port", 50058, "端口号") // 这个修改为0,如果我们从命令行带参数启动的话就不会为0

	//初始化
	initialize.InitLogger()
	initialize.InitConfig()
	initialize.InitDB()
	initialize.InitEs()
	zap.S().Info(global.ServerConfig)
	//省略。。。
  • 启动查看
    在这里插入图片描述
    在这里插入图片描述

5 - 同步已经的mysql数据到es中

  • goods_srv/model/main/main.go
package main

import (
	"context"
	"fmt"
	"github.com/olivere/elastic/v7"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"log"
	"nd/goods_srv/global"
	"nd/goods_srv/initialize"
	"nd/goods_srv/model"
	"os"
	"strconv"
	"time"
)

func main() {
	/*	initialize.InitConfig()
		dsn := fmt.Sprintf("root:jiushi@tcp(%s:3306)/mxshop_goods_srv?charset=utf8mb4&parseTime=True&loc=Local", global.ServerConfig.MysqlInfo.Host)

		newLogger := logger.New(
			log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
			logger.Config{
				SlowThreshold: time.Second, // 慢 SQL 阈值
				LogLevel:      logger.Info, // Log level
				Colorful:      true,        // 禁用彩色打印
			},
		)

		// 全局模式
		db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
			NamingStrategy: schema.NamingStrategy{
				SingularTable: true,
			},
			Logger: newLogger,
		})
		if err != nil {
			panic(err)
		}

		_ = db.AutoMigrate(&model.Category{},
			&model.Brands{}, &model.GoodsCategoryBrand{}, &model.Banner{}, &model.Goods{})*/
	Mysql2Es()
}

// Mysql2Es 将之前mysql的goods数据保存到es中
func Mysql2Es() {
	initialize.InitConfig()
	dsn := fmt.Sprintf("root:jiushi@tcp(%s:3306)/mxshop_goods_srv?charset=utf8mb4&parseTime=True&loc=Local", global.ServerConfig.MysqlInfo.Host)

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
		logger.Config{
			SlowThreshold: time.Second, // 慢 SQL 阈值
			LogLevel:      logger.Info, // Log level
			Colorful:      true,        // 禁用彩色打印
		},
	)

	// 全局模式
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			SingularTable: true,
		},
		Logger: newLogger,
	})
	if err != nil {
		panic(err)
	}

	host := fmt.Sprintf("http://%s:%d", global.ServerConfig.EsInfo.Host, global.ServerConfig.EsInfo.Port)
	loggerEs := log.New(os.Stdout, "mxshop", log.LstdFlags)
	global.EsClient, err = elastic.NewClient(elastic.SetURL(host), elastic.SetSniff(false),
		elastic.SetTraceLog(loggerEs))
	if err != nil {
		panic(err)
	}

	var goods []model.Goods
	db.Find(&goods)
	for _, g := range goods {
		esModel := model.EsGoods{
			ID:          g.ID,
			CategoryID:  g.CategoryID,
			BrandsID:    g.BrandsID,
			OnSale:      g.OnSale,
			ShipFree:    g.ShipFree,
			IsNew:       g.IsNew,
			IsHot:       g.IsHot,
			Name:        g.Name,
			ClickNum:    g.ClickNum,
			SoldNum:     g.SoldNum,
			FavNum:      g.FavNum,
			MarketPrice: g.MarketPrice,
			GoodsBrief:  g.GoodsBrief,
			ShopPrice:   g.ShopPrice,
		}
		_, err = global.EsClient.Index().
			Index(esModel.GetIndexName()).
			BodyJson(esModel).
			Id(strconv.Itoa(int(g.ID))).
			Do(context.Background())
		if err != nil {
			panic(err)
		}
		//强调一下 一定要将docker启动es的java_ops的内存设置大一些 否则运行过程中会出现 bad request错误
	}
}

在这里插入图片描述
在这里插入图片描述


三、goods接口集成es查询

1 - GoodsList中集成es

  • goods_srv/handler/handle_goods.go
func (s *GoodsServer) GoodsList(ctx context.Context, req *proto.GoodsFilterRequest) (*proto.GoodsListResponse, error) {
	//关键词搜索、查询新品、查询热门商品、通过价格区间筛选, 通过商品分类筛选
	goodsListResponse := &proto.GoodsListResponse{}

	//match bool 复合查询
	q := elastic.NewBoolQuery()
	localDB := global.DB.Model(model.Goods{})
	if req.KeyWords != "" {
		q = q.Must(elastic.NewMultiMatchQuery(req.KeyWords, "name", "goods_brief"))
	}
	if req.IsHot {
		localDB = localDB.Where(model.Goods{IsHot: true})
		q = q.Filter(elastic.NewTermQuery("is_hot", req.IsHot))
	}
	if req.IsNew {
		q = q.Filter(elastic.NewTermQuery("is_new", req.IsNew))
	}
	if req.PriceMin > 0 {
		q = q.Filter(elastic.NewRangeQuery("shop_price").Gte(req.PriceMin))
	}
	if req.PriceMax > 0 {
		q = q.Filter(elastic.NewRangeQuery("shop_price").Lte(req.PriceMax))
	}
	if req.Brand > 0 {
		q = q.Filter(elastic.NewTermQuery("brands_id", req.Brand))
	}

	//通过category去查询商品
	// 子查询 嵌套 子查询
	// SELECT * FROM goods WHERE category_id IN(SELECT id FROM category WHERE parent_category_id IN (SELECT id FROM category WHERE parent_category_id=1001))
	var subQuery string
	categoryIds := make([]interface{}, 0)
	if req.TopCategory > 0 {
		var category model.Category
		if result := global.DB.First(&category, req.TopCategory); result.RowsAffected == 0 {
			return nil, status.Errorf(codes.NotFound, "商品分类不存在")
		}
		if category.Level == 1 {
			subQuery = fmt.Sprintf("select id from category where parent_category_id in (select id from category WHERE parent_category_id=%d)", req.TopCategory)
		} else if category.Level == 2 {
			subQuery = fmt.Sprintf("select id from category WHERE parent_category_id=%d", req.TopCategory)
		} else if category.Level == 3 {
			subQuery = fmt.Sprintf("select id from category WHERE id=%d", req.TopCategory)
		}
		type Result struct {
			ID int32
		}
		var results []Result
		global.DB.Model(model.Category{}).Raw(subQuery).Scan(&results)
		for _, re := range results {
			categoryIds = append(categoryIds, re.ID)
		}

		//生成terms查询
		q = q.Filter(elastic.NewTermsQuery("category_id", categoryIds...))
	}

	//分页
	if req.Pages == 0 {
		req.Pages = 1
	}

	switch {
	case req.PagePerNums > 100:
		req.PagePerNums = 100
	case req.PagePerNums <= 0:
		req.PagePerNums = 10
	}
	result, err := global.EsClient.Search().Index(model.EsGoods{}.GetIndexName()).Query(q).From(int(req.Pages)).Size(int(req.PagePerNums)).Do(context.Background())
	if err != nil {
		return nil, err
	}

	goodsIds := make([]int32, 0)
	goodsListResponse.Total = int32(result.Hits.TotalHits.Value)
	for _, value := range result.Hits.Hits {
		goods := model.EsGoods{}
		_ = json.Unmarshal(value.Source, &goods)
		goodsIds = append(goodsIds, goods.ID)
	}

	//查询id在某个数组中的值
	var goods []model.Goods
	re := localDB.Preload("Category").Preload("Brands").Find(&goods, goodsIds)
	if re.Error != nil {
		return nil, re.Error
	}

	for _, good := range goods {
		goodsInfoResponse := ModelToResponse(good)
		goodsListResponse.Data = append(goodsListResponse.Data, &goodsInfoResponse)
	}

	return goodsListResponse, nil
}

2 - CreateGoods集成es

  • 为了降低耦合性:我们使用gorm的钩子函数,这样就可以不用修改CreateGoods的逻辑
  • goods_srv/model/goods.go:添加钩子函数
func (g *Goods) AfterCreate(tx *gorm.DB) (err error) {
	esModel := EsGoods{
		ID:          g.ID,
		CategoryID:  g.CategoryID,
		BrandsID:    g.BrandsID,
		OnSale:      g.OnSale,
		ShipFree:    g.ShipFree,
		IsNew:       g.IsNew,
		IsHot:       g.IsHot,
		Name:        g.Name,
		ClickNum:    g.ClickNum,
		SoldNum:     g.SoldNum,
		FavNum:      g.FavNum,
		MarketPrice: g.MarketPrice,
		GoodsBrief:  g.GoodsBrief,
		ShopPrice:   g.ShopPrice,
	}

	_, err = global.EsClient.Index().Index(esModel.GetIndexName()).BodyJson(esModel).Id(strconv.Itoa(int(g.ID))).Do(context.Background())
	if err != nil {
		return err
	}
	return nil
}
  • goods_srv/handler/handle_goods.go:需要添加上事务处理
func (s *GoodsServer) CreateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*proto.GoodsInfoResponse, error) {
	//省略。。。
	//srv之间互相调用了
	tx := global.DB.Begin()
	result := tx.Save(&goods) // Save的时候会自动调用钩子函数,所以在这里添加事务处理
	if result.Error != nil {
		tx.Rollback()
		return nil, result.Error
	}
	tx.Commit()
	return &proto.GoodsInfoResponse{
		Id: goods.ID,
	}, nil
}
  • YApi测试添加商品:需要开启goods_web服务、goods_srv服务
    在这里插入图片描述
  • kibana查询35的商品:添加成功
    在这里插入图片描述

3 - 商品更新与商品删除

  • goods_srv/model/goods.go:商品更新与删除同样使用gorm的钩子函数来实现
func (g *Goods) AfterUpdate(tx *gorm.DB) (err error) {
	esModel := EsGoods{
		ID:          g.ID,
		CategoryID:  g.CategoryID,
		BrandsID:    g.BrandsID,
		OnSale:      g.OnSale,
		ShipFree:    g.ShipFree,
		IsNew:       g.IsNew,
		IsHot:       g.IsHot,
		Name:        g.Name,
		ClickNum:    g.ClickNum,
		SoldNum:     g.SoldNum,
		FavNum:      g.FavNum,
		MarketPrice: g.MarketPrice,
		GoodsBrief:  g.GoodsBrief,
		ShopPrice:   g.ShopPrice,
	}

	_, err = global.EsClient.Update().Index(esModel.GetIndexName()).
		Doc(esModel).Id(strconv.Itoa(int(g.ID))).Do(context.Background())
	if err != nil {
		return err
	}
	return nil
}

func (g *Goods) AfterDelete(tx *gorm.DB) (err error) {
	_, err = global.EsClient.Delete().Index(EsGoods{}.GetIndexName()).Id(strconv.Itoa(int(g.ID))).Do(context.Background())
	if err != nil {
		return err
	}
	return nil
}
  • goods_srv/handler/handle_goods.go
// DeleteGoods 逻辑删除
func (s *GoodsServer) DeleteGoods(ctx context.Context, req *proto.DeleteGoodsInfo) (*emptypb.Empty, error) {
	if result := global.DB.Delete(&model.Goods{BaseModel: model.BaseModel{ID: req.Id}}, req.Id); result.Error != nil {
		return nil, status.Errorf(codes.NotFound, "商品不存在")
	}
	return &emptypb.Empty{}, nil
}

func (s *GoodsServer) UpdateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*emptypb.Empty, error) {
	var goods model.Goods

	if result := global.DB.First(&goods, req.Id); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "商品不存在")
	}

	var category model.Category
	if result := global.DB.First(&category, req.CategoryId); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "商品分类不存在")
	}

	var brand model.Brands
	if result := global.DB.First(&brand, req.BrandId); result.RowsAffected == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "品牌不存在")
	}

	goods.Brands = brand
	goods.BrandsID = brand.ID
	goods.Category = category
	goods.CategoryID = category.ID
	goods.Name = req.Name
	goods.GoodsSn = req.GoodsSn
	goods.MarketPrice = req.MarketPrice
	goods.ShopPrice = req.ShopPrice
	goods.GoodsBrief = req.GoodsBrief
	goods.ShipFree = req.ShipFree
	goods.Images = req.Images
	goods.DescImages = req.DescImages
	goods.GoodsFrontImage = req.GoodsFrontImage
	goods.IsNew = req.IsNew
	goods.IsHot = req.IsHot
	goods.OnSale = req.OnSale

	tx := global.DB.Begin()
	result := tx.Save(&goods)
	if result.Error != nil {
		tx.Rollback()
		return nil, result.Error
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}

四、完整源码

  • 完整源码下载mxshop_srvsV11.0rar
  • 源码说明:(nacos的ip配置自行修改,全局变量DEV_CONFIG设置:1=zsz,2=comp,3=home)
    • goods_srv/model/sql/mxshop_goods.sql:包含了建表语句
    • other_import/api.json:YApi的导入文件
    • other_import/nacos_config_export_user.zip:nacos的user配置集导入文件
    • other_import/nacos_config_export_goods.zip:nacos的goods配置集导入文件
    • other_import/nacos_config_export_inventory.zip:nacos的inventory的配置导入文件
    • other_import/nacos_config_export_orders.zip:nacos的orders的配置导入文件
    • other_import/nacos_config_export_userop.zip:nacos的userop的配置导入文件
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无休止符

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值