服务的具体编写
获取品牌信息的基础逻辑
我们为了便于测试,可以先把方法写成下面这样:
go
type GoodsServer struct {
proto.UnimplementedGoodsServer
}
之后再 test/brands.go 中进行编写测试代码:
go
// 创建客户端
var brandClient proto.GoodsClient
var conn *grpc.ClientConn
func Init() {
var err error
conn, 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 服务的编写:
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)
fmt.Println(result.RowsAffected)
return &brandListResponse, nil
}
这是一个很基础的查库的写法
添加返回的信息:
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)
if result.Error != nil {
return nil, result.Error
}
brandListResponse.Total = int32(result.RowsAffected)
// 构造返回信息
var brandInfoResponses []*proto.BrandInfoResponse
for _, brand := range brands {
var brandInfoResponse proto.BrandInfoResponse
brandInfoResponse.Id = brand.ID
brandInfoResponse.Name = brand.Name
brandInfoResponse.Logo = brand.Logo
brandInfoResponses = append(brandInfoResponses, &brandInfoResponse)
}
brandListResponse.Data = brandInfoResponses
return &brandListResponse, nil
}
分页信息的添加
向 handler中添加一个新的文件base.go 中添加分页基础方法:
handler/base.go
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 = 100
case pageSize <= 0:
pageSize = 10
}
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
由于添加分页信息后会导致result.RowAffects 取出来的数为当页数,我们还需要 global.DB.Model(&xxx{}).Count(&total) 来对数量进行统计。
全量的代码如下所示:
brands.go
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.BrandInfoResponse
for _, brand := range brands {
var brandInfoResponse proto.BrandInfoResponse
brandInfoResponse.Id = brand.ID
brandInfoResponse.Name = brand.Name
brandInfoResponse.Logo = brand.Logo
brandInfoResponses = append(brandInfoResponses, &brandInfoResponse)
}
brandListResponse.Data = brandInfoResponses
return &brandListResponse, nil
}
增、删、改 的代码在这里:
go
// 新建品牌
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 {
BaseModel
Name string `gorm:"type:varchar(20);not null;"` // 分类 名
ParentCategoryID int32 // 父分类ID
ParentCategory *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 {
BaseModel
Name string `gorm:"type:varchar(20);not null;"` // 分类 名
ParentCategoryID int32 // 父分类ID
ParentCategory *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 进行预加载:
迭代一:
go
// // 获取所有商品分类
// 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
}
注意,这里还没有结束,我们预加载之后出现了两个问题,第一个是我们的二级类目也被查询出来了,和一级类目放在了一起,第二个问题是我们只会往下查询一条,这里我们的解决方式是:
go
// // 获取所有商品分类
// 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 {
BaseModel
Name string `gorm:"type:varchar(20);not null;" json:"name"` // 分类 名
ParentCategoryID int32 `json:"parent"` // 父分类ID
ParentCategory *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 的灵活写法:
go
// 获取子分类
func (s *GoodsServer) GetSubCategory(ctx context.Context, req *proto.CategoryListRequest) (*proto.SubCategoryListResponse, error) {
// 构造需要的返回内容
categoryListResponse := proto.SubCategoryListResponse{}
// 查找对应的分类是否在数据库中存在
var category model.Category
if 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.Category
preloadLevel := "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 = subCategoryInfoResponse
return &categoryListResponse, nil
}
分类的其他接口
此处注意,更新接口必须进行传递判断,因为 proto 具备默认值,若不进行判断,会出现误更新的情况
分类-品牌关联接口
知识点:Preload 在 外键场景下的应用
category_brand.go
go
package handler
import (
"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.GoodsCategoryBrand
categoryBrandListResponse := proto.CategoryBrandListResponse{}
// 取出分页数据的总数
var total int64
global.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.CategoryBrandResponse
for _, 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 = categoryBrandResponses
return &categoryBrandListResponse, nil
}
// 功能点接口:
// 取出某个分类下的所有品牌
func (s *GoodsServer) GetCategoryBrandList(ctx context.Context, req *proto.CategoryInfoRequest) (*proto.BrandListResponse, error) {
brandListResponse := proto.BrandListResponse{}
var category model.GoodsCategoryBrand
var categoryBrands []model.GoodsCategoryBrand
var 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 = brandInfo
return &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.Category
if result := global.DB.Find(&category, req.CategoryId); result.RowsAffected == 0 {
return nil, status.Errorf(codes.InvalidArgument, "未找到对应的分类信息")
}
var brand model.Brands
if 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.GoodsCategoryBrand
if result := global.DB.First(categoryBrand, req.Id); result.RowsAffected == 0 {
return nil, status.Errorf(codes.NotFound, "为找到要更新的商品分类")
}
var brand model.Brands
if result := global.DB.First(&brand, req.BrandId); 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.NotFound, "未找到匹配的分类相关信息")
}
categoryBrand.BrandsID = req.BrandId
categoryBrand.CategoryID = req.CategoryId
global.DB.Save(categoryBrand)
return &emptypb.Empty{}, nil
}
商品接口
知识点:多条件拼接查询,条件动态变化时请况的处理方式
SQL语句、子查询的拼接
go
package handler
import (
"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 string
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", 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 int64
localDB.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.Goods
if 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.Category
if result := global.DB.First(&category, req.CategoryId); result.RowsAffected == 0 {
return nil, status.Errorf(codes.NotFound, "未找到应插入的分类信息")
}
var brands model.Brands
if 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, // 注意此处照片的上传是使用第三方技术进行上传的,此处仅为照片存储url
DescImages: 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.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.NotFound, "未找到对应分类")
}
var brands model.Brands
if result := global.DB.First(&brands, req.BrandId); result.RowsAffected == 0 {
return nil, status.Errorf(codes.NotFound, "未找到对应品牌")
}
goods.Brands = brands
goods.BrandsID = brands.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.Images = req.Images
goods.DescImages = req.DescImages
goods.GoodsFrontImage = req.GoodsFrontImage
goods.IsNew = req.IsNew
goods.IsHot = req.IsHot
goods.OnSale = req.OnSale
global.DB.Save(&goods)
return &emptypb.Empty{}, nil
}