服务的具体编写
获取品牌信息的基础逻辑
我们为了便于测试,可以先把方法写成下面这样:
type GoodsServer struct {proto.UnimplementedGoodsServer
}
之后再 test/brands.go 中进行编写测试代码:
// 创建客户端
var brandClient proto.GoodsClient
var conn *grpc.ClientConnfunc Init() {var err errorconn, err = grpc.Dial("127.0.0.1:50051", grpc.WithInsecure())if err != nil {panic(err)}brandClient = proto.NewGoodsClient(conn)
}func main() {Init()TestGetBrandList()conn.Close()
}/**
* 测试获取用户信息*/func TestGetBrandList() {rsp, err := brandClient.BrandList(context.Background(), &proto.BrandFilterRequest{})if err != nil {panic(err)}for _, brand := range rsp.Data {fmt.Println(brand.Name)}
}
这里就会输出一项:
panic: rpc error: code = Unimplemented desc = method BrandList not implemented
便于我们提前测试代码连通性
brands.go 服务的编写:
// 注意这里,所有的方法都绑定到 goods.go 中的 GoodsServer 类上
// 所有的方法都会需要context 和 需要的参数
// 注意这里是 GRPC 协议,不是 HTTP 协议了
func (s *GoodsServer) BrandList(context context.Context, req *proto.BrandFilterRequest) (*proto.BrandListResponse, error) {// 这里一般都是需要定义一个用来返回的变量var brandListResponse proto.BrandListResponse// 定义一个List用来存储取出来的集合var brands []model.Brandsresult := global.DB.Find(&brands)fmt.Println(result.RowsAffected)return &brandListResponse, nil
}
这是一个很基础的查库的写法
添加返回的信息:
// 注意这里,所有的方法都绑定到 goods.go 中的 GoodsServer 类上
// 所有的方法都会需要context 和 需要的参数
// 注意这里是 GRPC 协议,不是 HTTP 协议了
func (s *GoodsServer) BrandList(context context.Context, req *proto.BrandFilterRequest) (*proto.BrandListResponse, error) {// 这里一般都是需要定义一个用来返回的变量var brandListResponse proto.BrandListResponse// 定义一个List用来存储取出来的集合// 数据库中取出数据var brands []model.Brandsresult := global.DB.Find(&brands)if result.Error != nil {return nil, result.Error}brandListResponse.Total = int32(result.RowsAffected)// 构造返回信息var brandInfoResponses []*proto.BrandInfoResponsefor _, brand := range brands {var brandInfoResponse proto.BrandInfoResponsebrandInfoResponse.Id = brand.IDbrandInfoResponse.Name = brand.NamebrandInfoResponse.Logo = brand.LogobrandInfoResponses = append(brandInfoResponses, &brandInfoResponse)}brandListResponse.Data = brandInfoResponsesreturn &brandListResponse, nil
}
分页信息的添加
向 handler中添加一个新的文件base.go 中添加分页基础方法:
handler/base.go
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {return func (db *gorm.DB) *gorm.DB{if page == 0 {page = 1}switch {case pageSize > 100:pageSize = 100case pageSize <= 0:pageSize = 10}offset := (page - 1) * pageSizereturn db.Offset(offset).Limit(pageSize)}
}
由于添加分页信息后会导致result.RowAffects 取出来的数为当页数,我们还需要 global.DB.Model(&xxx{}).Count(&total) 来对数量进行统计。
全量的代码如下所示:
brands.go
// 注意这里,所有的方法都绑定到 goods.go 中的 GoodsServer 类上
// 所有的方法都会需要context 和 需要的参数
// 注意这里是 GRPC 协议,不是 HTTP 协议了
func (s *GoodsServer) BrandList(context context.Context, req *proto.BrandFilterRequest) (*proto.BrandListResponse, error) {// 这里一般都是需要定义一个用来返回的变量var brandListResponse proto.BrandListResponse// 定义一个List用来存储取出来的集合// 数据库中取出数据var brands []model.Brands//result := global.DB.Find(&brands)// 获取全量数据个数信息var total int64// DB.Model 是一个非常好用,又非常危险的方法,其可以对数据库进行全量查询、删除、修改,也可以查询数量(常用)global.DB.Model(&model.Brands{}).Count(&total)// 超绝分页,巨简单result := global.DB.Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Find(&brands)if result.Error != nil {return nil, result.Error}brandListResponse.Total = int32(total)// 构造返回信息var brandInfoResponses []*proto.BrandInfoResponsefor _, brand := range brands {var brandInfoResponse proto.BrandInfoResponsebrandInfoResponse.Id = brand.IDbrandInfoResponse.Name = brand.NamebrandInfoResponse.Logo = brand.LogobrandInfoResponses = append(brandInfoResponses, &brandInfoResponse)}brandListResponse.Data = brandInfoResponsesreturn &brandListResponse, nil
}
增、删、改 的代码在这里:
// 新建品牌
func (s *GoodsServer) CreateBrand(ctx context.Context, req *proto.BrandRequest) (*proto.BrandInfoResponse, error) {// 此处有一个逻辑,品牌不能同名,先查询是否有同名记录if result := global.DB.First(&model.Brands{}); result.RowsAffected >= 1 {// 证明有同名,应返回错误// 这里的后半部分是 google 包下的内容,其代表错误的信息码return nil, status.Errorf(codes.InvalidArgument, "品牌已存在")}// 若未出现重名问题,则执行插入brand := &model.Brands{Name: req.Name,Logo: req.Logo,}global.DB.Create(brand)// 保存完毕后,更新返回信息return &proto.BrandInfoResponse{Id: brand.ID}, nil
}// 删除品牌
func (s *GoodsServer) DeleteBrand(ctx context.Context, req *proto.BrandRequest) (*emptypb.Empty, error) {// 直接开删if result := global.DB.Delete(&model.Brands{}, req.Id); result.RowsAffected < 1 {return nil, status.Errorf(codes.InvalidArgument, "删除失败,对应品牌不存在")}return &emptypb.Empty{}, nil
}// 更新品牌
func (s *GoodsServer) UpdateBrand(ctx context.Context, req *proto.BrandRequest) (*emptypb.Empty, error) {brands := model.Brands{}if result := global.DB.First(&brands, req.Id); result.RowsAffected < 1 {return nil, status.Errorf(codes.InvalidArgument, "您指定更新的品牌不存在")}if req.Name != "" {brands.Name = req.Name}if req.Logo != "" {brands.Logo = req.Logo}global.DB.Save(&brands)return &emptypb.Empty{}, nil
}
轮播图部分过于简单,此处不再重复定义
子分类在 GORM 中的快速处理方式
假设我们要用到子分类的功能需求,例如下面这样:
[分类名:电子产品,父分类ID:xxx子分类:[分类名:xxx父分类ID:xxx子分类:[...]]
]
我们就可以使用GORM中的预加载功能快速处理分类场景:
下面是我们的分类接口的数据结构:
type Category struct {BaseModelName string `gorm:"type:varchar(20);not null;"` // 分类 名ParentCategoryID int32 // 父分类IDParentCategory *Category // 父分类对象 此处因为是自己指向自己,必须使用指针Level int32 `gorm:"type:int;not null;default:1"` // 分类级别IsTab bool `gorm:"default:false;not null"` // 是否显示在 Tab 栏
}
下面的需求是:让我们获取数据时,可以直接获取子数据,就像上面的集合一样
我们这样添加:SubCategory []*Category gorm:"foreignKey:ParentCategoryID;references:ID"
foreignKey 指的是自己的ParentCategoryID应该去关联谁,而references指的是这个 foreignKey 应该去找谁,也就是说,根据自己的ParentCategoryID来推断应该在谁的ID下面
// 商品分类数据对象:一级分类、二级分类...
type Category struct {BaseModelName string `gorm:"type:varchar(20);not null;"` // 分类 名ParentCategoryID int32 // 父分类IDParentCategory *Category // 父分类对象 此处因为是自己指向自己,必须使用指针SubCategory []*Category `gorm:"foreignKey:ParentCategoryID;references:ID"`Level int32 `gorm:"type:int;not null;default:1"` // 分类级别IsTab bool `gorm:"default:false;not null"` // 是否显示在 Tab 栏
}
之后我们进行DB.Preload 进行预加载:
迭代一:
// // 获取所有商品分类
// GetAllCategorysList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CategoryListResponse, error)
func (s *GoodsServer) GetAllCategorysList(ctx context.Context, empty *emptypb.Empty) (*proto.CategoryListResponse, error) {var categorys []model.Category// 进行预加载,预加载出所需要的数据global.DB.Preload("SubCategory").Find(&categorys)// 尝试打印for _, category := range categorys {fmt.Println(category)}return nil, nil
}
注意,这里还没有结束,我们预加载之后出现了两个问题,第一个是我们的二级类目也被查询出来了,和一级类目放在了一起,第二个问题是我们只会往下查询一条,这里我们的解决方式是:
// // 获取所有商品分类
// GetAllCategorysList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CategoryListResponse, error)
func (s *GoodsServer) GetAllCategorysList(ctx context.Context, empty *emptypb.Empty) (*proto.CategoryListResponse, error) {var categorys []model.Category// 进行预加载,预加载出所需要的数据// 由于分级最多到 3 ,所以这里写一个 SubCategory.SubCategory 就可以完全覆盖 3 级了,如果最多到 4 级的话,就必须再写一个 .SubCategory 了global.DB.Where(&model.Category{Level: 1}).Preload("SubCategory.SubCategory").Find(&categorys)// 利用预留的 JSON 字段进行返回b, _ := json.Marshal(&categorys)return &proto.CategoryListResponse{JsonData: string(b)}, nil
}
由于我们预留出来了给前端的 JSON 字段,故我们可以将我们取得的数据装填到 JsonData 字段中。
另外:我们可以对我们想要的数据在 model 的位置进行格式化
// 商品分类数据对象:一级分类、二级分类...
type Category struct {BaseModelName string `gorm:"type:varchar(20);not null;" json:"name"` // 分类 名ParentCategoryID int32 `json:"parent"` // 父分类IDParentCategory *Category `json:"-"` // 父分类对象 此处因为是自己指向自己,必须使用指针SubCategory []*Category `gorm:"foreignKey:ParentCategoryID;references:ID" json:"sub_category"`Level int32 `gorm:"type:int;not null;default:1" json:"level"` // 分类级别IsTab bool `gorm:"default:false;not null" json:"is_tab"` // 是否显示在 Tab 栏
}
子分类接口的编写,涉及到多层 preload 的灵活写法:
// 获取子分类
func (s *GoodsServer) GetSubCategory(ctx context.Context, req *proto.CategoryListRequest) (*proto.SubCategoryListResponse, error) {// 构造需要的返回内容categoryListResponse := proto.SubCategoryListResponse{}// 查找对应的分类是否在数据库中存在var category model.Categoryif result := global.DB.First(&category, req.Id); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "商品分类不存在")}// 构造要查找子分类的分类categoryListResponse.Info = &proto.CategoryInfoResponse{Id: category.ID,Name: category.Name,Level: category.Level,IsTab: category.IsTab,ParentCategory: category.ParentCategoryID,}// 查找子分类,这里有一个要注意的逻辑:// 我们必须选定分类的等级,若分类等级为一的话就意味着他可能有两级子分类,若分类等级为二的话,其就只可能有一级子分类var subCategoryList []model.CategorypreloadLevel := "SubCategory"if category.Level == 1 {preloadLevel = "SubCategory.Subcategory"}global.DB.Where(&model.Category{ParentCategoryID: req.Id}).Preload(preloadLevel).Find(&subCategoryList)var subCategoryInfoResponse []*proto.CategoryInfoResponse// 拼接字段:for _, subCategory := range subCategoryList {subCategoryInfoResponse = append(subCategoryInfoResponse, &proto.CategoryInfoResponse{Id: subCategory.ID,Name: subCategory.Name,ParentCategory: subCategory.ParentCategoryID,Level: subCategory.Level,IsTab: subCategory.IsTab,})}categoryListResponse.SubCategorys = subCategoryInfoResponsereturn &categoryListResponse, nil
}
分类的其他接口
此处注意,更新接口必须进行传递判断,因为 proto 具备默认值,若不进行判断,会出现误更新的情况
分类-品牌关联接口
知识点:Preload 在 外键场景下的应用
category_brand.go
package handlerimport ("context""google.golang.org/grpc/codes""google.golang.org/grpc/status""google.golang.org/protobuf/types/known/emptypb""mxshop_srvs/goods_srv/global""mxshop_srvs/goods_srv/model""mxshop_srvs/goods_srv/proto"
)// 全量接口:
// 取出所有品牌与分类的相关列表
func (s *GoodsServer) CategoryBrandList(ctx context.Context, req *proto.CategoryBrandFilterRequest) (*proto.CategoryBrandListResponse, error) {// 构造基础数据var categoryBrands []model.GoodsCategoryBrandcategoryBrandListResponse := proto.CategoryBrandListResponse{}// 取出分页数据的总数var total int64global.DB.Model(&model.GoodsCategoryBrand{}).Count(&total)categoryBrandListResponse.Total = int32(total)// 全量// 注意这里必须添加 Preload 以便对于外键的相关信息进行存取的操作,因为元模型中有嵌套两层的环节global.DB.Preload("Category").Preload("Brands").Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Find(&categoryBrands)// 根据要返回的信息构造需要返回的数据// 对于所有需要返回的数据来讲,我们需要遍历我们从数据库中取出的信息,来对返回进行拼装var categoryBrandResponses []*proto.CategoryBrandResponsefor _, categoryBrandItem := range categoryBrands {categoryBrandResponses = append(categoryBrandResponses, &proto.CategoryBrandResponse{Category: &proto.CategoryInfoResponse{Id: categoryBrandItem.CategoryID,Name: categoryBrandItem.Category.Name,ParentCategory: categoryBrandItem.Category.ParentCategoryID,Level: categoryBrandItem.Category.Level,IsTab: categoryBrandItem.Category.IsTab,},Brand: &proto.BrandInfoResponse{Id: categoryBrandItem.Brands.ID,Name: categoryBrandItem.Brands.Name,Logo: categoryBrandItem.Brands.Logo,},})}// 封装数据categoryBrandListResponse.Data = categoryBrandResponsesreturn &categoryBrandListResponse, nil
}// 功能点接口:
// 取出某个分类下的所有品牌
func (s *GoodsServer) GetCategoryBrandList(ctx context.Context, req *proto.CategoryInfoRequest) (*proto.BrandListResponse, error) {brandListResponse := proto.BrandListResponse{}var category model.GoodsCategoryBrandvar categoryBrands []model.GoodsCategoryBrandvar brandInfo []*proto.BrandInfoResponse// 先尝试查询分类,看分类是否存在if result := global.DB.Find(&category, req); result.RowsAffected == 0 {return nil, status.Errorf(codes.InvalidArgument, "所选商品分类不存在")}// 通过商品品牌关联表查询所有该分类下的品牌//if result := global.DB.Preload("Brands").Where(&model.GoodsCategoryBrand{CategoryID: category.ID}).Find(&categoryBrands); result.RowsAffected > 0 {brandListResponse.Total = int32(result.RowsAffected)}// 拼接下一段返回for _, brandItem := range categoryBrands {brandInfo = append(brandInfo, &proto.BrandInfoResponse{Id: brandItem.Brands.ID,Name: brandItem.Brands.Name,Logo: brandItem.Brands.Logo,})}// 构造返回内容brandListResponse.Data = brandInforeturn &brandListResponse, nil
}// 新建品牌分类
// CreateCategoryBrand(ctx context.Context, in *CategoryBrandRequest, opts ...grpc.CallOption) (*CategoryBrandResponse, error)
func (s *GoodsServer) CreateCategoryBrand(ctx context.Context, req *proto.CategoryBrandRequest) (*proto.CategoryBrandResponse, error) {// 确认分类和品牌存在才能继续操作var category model.Categoryif result := global.DB.Find(&category, req.CategoryId); result.RowsAffected == 0 {return nil, status.Errorf(codes.InvalidArgument, "未找到对应的分类信息")}var brand model.Brandsif result := global.DB.Find(&brand, req.BrandId); result.RowsAffected == 0 {return nil, status.Errorf(codes.InvalidArgument, "未找到对应的品牌信息")}// 一切准备就绪,执行插入操作categoryBrand := model.GoodsCategoryBrand{CategoryID: req.CategoryId,BrandsID: req.BrandId,}global.DB.Save(&categoryBrand)return &proto.CategoryBrandResponse{Id: categoryBrand.ID,}, nil
}// 删除品牌分类的关联关系
// DeleteCategoryBrand(ctx context.Context, in *CategoryBrandRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
func (s *GoodsServer) DeleteCategoryBrand(ctx context.Context, req *proto.CategoryBrandRequest) (*emptypb.Empty, error) {if result := global.DB.Delete(&model.GoodsCategoryBrand{}, req.Id); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "所指定的品牌分类不存在")}return &emptypb.Empty{}, nil
}// 更新品牌分类信息
// UpdateCategoryBrand(ctx context.Context, in *CategoryBrandRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
func (s *GoodsServer) UpdateCategoryBrand(ctx *context.Context, req *proto.CategoryBrandRequest) (*emptypb.Empty, error) {var categoryBrand model.GoodsCategoryBrandif result := global.DB.First(categoryBrand, req.Id); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "为找到要更新的商品分类")}var brand model.Brandsif result := global.DB.First(&brand, req.BrandId); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到匹配的相关品牌信息")}var category model.Categoryif result := global.DB.First(&category, req.CategoryId); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到匹配的分类相关信息")}categoryBrand.BrandsID = req.BrandIdcategoryBrand.CategoryID = req.CategoryIdglobal.DB.Save(categoryBrand)return &emptypb.Empty{}, nil
}
商品接口
知识点:多条件拼接查询,条件动态变化时请况的处理方式
SQL语句、子查询的拼接
package handlerimport ("context""fmt""google.golang.org/grpc/codes""google.golang.org/grpc/status""google.golang.org/protobuf/types/known/emptypb""mxshop_srvs/goods_srv/global""mxshop_srvs/goods_srv/model""mxshop_srvs/goods_srv/proto"
)type GoodsServer struct {proto.UnimplementedGoodsServer
}// 数据库对象转返回对象
func ModelToResponse(goods model.Goods) proto.GoodsInfoResponse {return proto.GoodsInfoResponse{Id: goods.ID,CategoryId: goods.CategoryID,Name: goods.Name,GoodsSn: goods.GoodsSn,ClickNum: goods.ClickNum,SoldNum: goods.SoldNum,FavNum: goods.FavNum,MarketPrice: goods.MarketPrice,ShopPrice: goods.ShopPrice,GoodsBrief: goods.GoodsBrief,ShipFree: goods.ShipFree,Images: goods.Images,DescImages: goods.DescImages,GoodsFrontImage: goods.GoodsFrontImage,IsNew: goods.IsNew,IsHot: goods.IsHot,OnSale: goods.OnSale,Category: &proto.CategoryBriefInfoResponse{Id: goods.Category.ID,Name: goods.Category.Name,},Brand: &proto.BrandInfoResponse{Id: goods.Brands.ID,Name: goods.Brands.Name,Logo: goods.Brands.Logo,},}}// // 商品部分
// // 获取商品的接口,包括条件获取
// func (s *GoodsServer) GoodsList(context.Context, *proto.GoodsFilterRequest) (*proto.GoodsListResponse, error) {}
// 其难点在于条件的拼接
func (s *GoodsServer) GoodsList(ctx context.Context, req *proto.GoodsFilterRequest) (*proto.GoodsListResponse, error) {goodsListResponse := &proto.GoodsListResponse{}var goods []model.Goods// 添加条件判断的拼接对象 queryMap 方式 但要注意的问题是: queryMap 仅仅适合于简单的相等条件的条件拼接,对于复杂条件其无能为力//queryMap := map[string]interface{}{}////if req.KeyWords != "" {// queryMap["name"] = "%" + req.KeyWords + "%"//}//if req.IsHot == true {// queryMap["is_hot"] = true//}// 我们利用 global.DB 来进行条件的拼接// 这里要先试用 Model 指定表名,因为我们下面的条件拼凑是需要先完全拼凑完再进行查询的localDB := global.DB.Model(&model.Goods{})// 一旦有条件成立时,我们就进行拼接if req.KeyWords != "" {localDB = localDB.Where("name LIKE ?", ""+req.KeyWords+"")}if req.IsHot {localDB = localDB.Where("is_hot = true")}if req.IsNew {localDB = localDB.Where(model.Goods{IsNew: true})}if req.PriceMin > 0 {localDB = localDB.Where("shop_price > ?", req.PriceMin)}if req.PriceMax > 0 {localDB = localDB.Where("shop_price < ?", req.PriceMax)}// SQL语句、子查询的拼接var subQuery stringif req.TopCategory > 0 {var category model.Categoryif 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", category.ID)} else if category.Level == 2 {subQuery = fmt.Sprintf("SELECT id FROM category WHERE parent_category_id in (%d)", category.ID)} else if category.Level == 3 {subQuery = fmt.Sprintf("SELECT id FROM category WHERE id in (%d)", category.ID)}localDB.Where(fmt.Sprintf("category_id in (%s)"), subQuery)}// 计数 利用 Count 方法,注意这里必须在之前进行计数var count int64localDB.Count(&count)goodsListResponse.Total = int32(count)// 分页查询具体数据// 由于 goods 中涉及到外键,需要使用预加载result := localDB.Preload("Category").Preload("Brands").Scopes(Paginate(int(req.Pages), int(req.PagePerNums))).Find(&goods)if result.Error != nil {return nil, result.Error}for _, goodItem := range goods {goodsResp := ModelToResponse(goodItem)goodsListResponse.Data = append(goodsListResponse.Data, &goodsResp)}return goodsListResponse, nil
}// // 批量查询商品信息的接口,避免查商品时发生一个一个调用服务、一条一条查的低效情况
// BatchGetGoods(context.Context, *BatchGoodsIdInfo) (*GoodsListResponse, error)
func (s *GoodsServer) BatchGetGoods(ctx context.Context, req *proto.BatchGoodsIdInfo) (*proto.GoodsListResponse, error) {goodsListResponse := &proto.GoodsListResponse{}var goods []model.Goods// 取出所有的信息result := global.DB.Where(req.Id).Find(&goods)for _, goodItem := range goods {goodResp := ModelToResponse(goodItem)goodsListResponse.Data = append(goodsListResponse.Data, &goodResp)}goodsListResponse.Total = int32(result.RowsAffected)return goodsListResponse, nil
}// // 获取商品信息(单独获取) 获取详情
// GetGoodsDetail(context.Context, *GoodInfoRequest) (*GoodsInfoResponse, error)
func (s *GoodsServer) GetGoodsDetail(ctx context.Context, req *proto.GoodInfoRequest) (*proto.GoodsInfoResponse, error) {var goods model.Goodsif result := global.DB.First(&goods, req.Id); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到对应商品信息")}goodsInfoResponse := ModelToResponse(goods)return &goodsInfoResponse, nil
}// // 添加商品
// CreateGoods(context.Context, *CreateGoodsInfo) (*GoodsInfoResponse, error)
func (s *GoodsServer) CreateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*proto.GoodsInfoResponse, error) {// 需要先行判断 分类、品牌信息是否存在var category model.Categoryif result := global.DB.First(&category, req.CategoryId); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到应插入的分类信息")}var brands model.Brandsif result := global.DB.First(&brands, req.BrandId); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到相关品牌信息")}goods := model.Goods{CategoryID: category.ID,Category: category,BrandsID: brands.ID,Brands: brands,ShipFree: req.ShipFree,IsNew: req.IsNew,IsHot: req.IsHot,OnSale: req.OnSale,Name: req.Name,GoodsSn: req.GoodsSn,MarketPrice: req.MarketPrice,ShopPrice: req.ShopPrice,GoodsBrief: req.GoodsBrief,Images: req.Images, // 注意此处照片的上传是使用第三方技术进行上传的,此处仅为照片存储urlDescImages: req.DescImages,GoodsFrontImage: req.GoodsFrontImage,}global.DB.Save(&goods)return &proto.GoodsInfoResponse{Id: goods.ID,}, nil
}// // 删除商品,没有明确需要返回的信息,返回一个占位符
// DeleteGoods(context.Context, *DeleteGoodsInfo) (*emptypb.Empty, error)
func (s *GoodsServer) DeleteGoods(ctx context.Context, req *proto.DeleteGoodsInfo) (*emptypb.Empty, error) {if result := global.DB.Delete(&model.Goods{}, req.Id); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到应删除的商品")}return &emptypb.Empty{}, nil
}// // 更新商品信息
// UpdateGoods(context.Context, *CreateGoodsInfo) (*emptypb.Empty, error)
func (s *GoodsServer) UpdateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*emptypb.Empty, error) {// 找到要更新的商品、分类、品牌信息,并判断他们是否存在var goods model.Goodsif result := global.DB.First(&goods, req.Id); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到对应商品")}var category model.Categoryif result := global.DB.First(&category, req.CategoryId); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到对应分类")}var brands model.Brandsif result := global.DB.First(&brands, req.BrandId); result.RowsAffected == 0 {return nil, status.Errorf(codes.NotFound, "未找到对应品牌")}goods.Brands = brandsgoods.BrandsID = brands.IDgoods.Category = categorygoods.CategoryID = category.IDgoods.Name = req.Namegoods.GoodsSn = req.GoodsSngoods.MarketPrice = req.MarketPricegoods.ShopPrice = req.ShopPricegoods.GoodsBrief = req.GoodsBriefgoods.Images = req.Imagesgoods.DescImages = req.DescImagesgoods.GoodsFrontImage = req.GoodsFrontImagegoods.IsNew = req.IsNewgoods.IsHot = req.IsHotgoods.OnSale = req.OnSaleglobal.DB.Save(&goods)return &emptypb.Empty{}, nil
}