目录
- 一、go语言中使用ES
- 1 - 使用第三方库
- 2 - 解析出查询结果
- 3 - es对象转换为struct
- 4 - 向es中添加数据
- 5 - 新建mapping
- 二、项目中集成ES
- 1 - 集成es接口分析
- 2 - 建立商品对应的struct和mapping
- 3 - nacos新增es配置
- 4 - 初始化es
- 5 - 同步已经的mysql数据到es中
- 三、goods接口集成es查询
- 1 - GoodsList中集成es
- 2 - CreateGoods集成es
- 3 - 商品更新与商品删除
- 四、完整源码
一、go语言中使用ES
1 - 使用第三方库
- github上搜索:go elasticsearch;我们会使用第三方的,因为第三方的使用会比官方的简单一些
-
olivere/elastic:https://github.com/olivere/elastic
-
olivere/elastic文档地址:https://olivere.github.io/elastic/
- 简单使用
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 {
}
}
文章来源:https://uudwc.com/A/9X05
二、项目中集成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错误
}
}
文章来源地址https://uudwc.com/A/9X05
三、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的配置导入文件