大家好,今天咱们继续完善商品服务里的商品规格模块。
众所周知,一个电商的商品设计是比较复杂的,咱们这里不过多的深究商品设计的每个表是否合理,是否漏写之类的问题,主要是为了搞明白 kratos 的使用和微服务相关的调用关系。当然我真正的编写时也会尽可能的让此项目的商品设计合理一些。但大量的表设计呀,重复性的 curd 就不会再文章中体现了,具体的代码参看 GitHub  上的源码。当然你觉得不合理的地方,欢迎给项目提 PR。
商品规格参数 商品参数,也有人管它们叫商品规格参数,信息如下图所示,一般可以分为规格分组、规格属性及属性值。这些特殊的规格参数,会影响商品 SKU 的信息,  我们选择不同的颜色、版本等规格,会影响我们 SKU 的记录,也就是对应的销售价格和商品的库存量。
商品属性参数信息如下图所示,一般可以分为分组、属性及属性值。这些信息基本不影响商品 SKU,只是作为商品的一些参数信息展示。
咱们这里为了方便商品的管理,使得数据更加有规律,实现更好的弹性设计,各自设置为一个模块。然后每个单独的模块都会跟上一篇文章中创建的商品类型进行关联。在创建一个具体的商品的时候,更好的使用商品类型下的商品规格以及商品属性信息。
编写代码 设计商品规格表 
data 层新增 specifications.go 文件 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package  dataimport  ("context" "errors" "goods/internal/biz" "goods/internal/domain" "time" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" type  SpecificationsAttr struct  {int64           `gorm:"primarykey;type:int" json:"id"` int64           `gorm:"index:type_id;type:int;comment:商品类型ID;not null"` string          `gorm:"type:varchar(250);not null;comment:规格参数名称" json:"name"` int32           `gorm:"comment:规格排序;default:99;not null;type:int" json:"sort"` bool            `gorm:"comment:参数状态;default:false" json:"status"` bool            `gorm:"comment:是否通用的SKU持有;default:false" json:"is_sku"` bool            `gorm:"comment:是否可查询;default:false" json:"is_select"` `gorm:"column:add_time" json:"created_at"` `gorm:"column:update_time" json:"updated_at"` `json:"deleted_at"` type  SpecificationsAttrValue struct  {int64           `gorm:"primarykey;type:int" json:"id"` int64           `gorm:"index:attr_id;type:int;comment:规格ID;not null"` string          `gorm:"type:varchar(250);not null;comment:规格参数信息值" json:"value"` int32           `gorm:"comment:规格参数值排序;default:99;not null;type:int" json:"sort"` `gorm:"column:add_time" json:"created_at"` `gorm:"column:update_time" json:"updated_at"` `json:"deleted_at"` type  specificationRepo struct  {func  NewSpecificationRepo (data *Data, logger log.Logger)  biz .SpecificationRepo return  &specificationRepo{
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 syntax = "proto3" ;service  Goods  rpc  CreateGoodsSpecification(SpecificationRequest) returns (SpecificationResponse)message  SpecificationValue  int64  id = 1 ;int64  attrId = 2 ;string  value = 3  [(validate.rules).string .min_len = 3 ];int32  sort = 4  [(validate.rules).string .min_len = 3 ];message  SpecificationRequest  int64  id = 1 ;int64  typeId = 2  [(validate.rules).string .min_len = 1 ];string  name = 3  [(validate.rules).string .min_len = 3 ];int32  sort = 4  [(validate.rules).string .min_len = 1 ];bool  status = 5 ;bool  isSku = 6 ;bool  isSelect = 7 ;repeated  SpecificationValue specificationValue = 8 ;message  SpecificationResponse  int64  id = 1 ;
service 层新增 specifications.go 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package  serviceimport  ("context" "goods/api/goods/v1" "goods/internal/domain" func  (g *GoodsService)  CreateGoodsSpecification (ctx context.Context, r *v1.SpecificationRequest)  (*v1.SpecificationResponse, error) var  value []*domain.SpecificationValueif  r.SpecificationValue != nil  {for  _, v := range  r.SpecificationValue {append (value, res)if  err != nil  {return  nil , errreturn  &v1.SpecificationResponse{nil 
domain 层新增 specifications.go 
 
这里上一篇介绍的 domain 又出现,开始在 domain 编写一个逻辑吧
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package  domaintype  Specification struct  {int64 int64 string int32 bool bool bool type  SpecificationValue struct  {int64 int64 string int32 func  (b *Specification)  IsTypeIDEmpty ()  bool return  b.TypeID == 0 func  (b *Specification)  IsValueEmpty ()  bool return  b.SpecificationValue == nil 
biz 层新增 specifications.go 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package  bizimport  ("context" "errors" "goods/internal/domain" "github.com/go-kratos/kratos/v2/log" type  SpecificationRepo interface  {int64 , error)int64 , []*domain.SpecificationValue) errortype  SpecificationUsecase struct  {func  NewSpecificationUsecase (repo SpecificationRepo, type  GoodsTypeRepo, tx Transaction, 	logger log.Logger)  *SpecificationUsecase return  &SpecificationUsecase{type ,func  (s *SpecificationUsecase)  CreateSpecification (ctx context.Context, r *domain.Specification)  (int64 , error) var  (int64 if  r.IsTypeIDEmpty() {return  id, errors.New("请选择商品类型进行绑定" )if  r.IsValueEmpty() {return  id, errors.New("请填写商品规格下的参数" )if  err != nil  {return  id, errfunc (ctx context.Context)  error if  err != nil  {return  errif  err != nil  {return  errreturn  nil return  id, err
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 type  GoodsTypeRepo interface  {int64 ) (*domain.GoodsType, error)func  (g *goodsTypeRepo)  IsExistsByID (ctx context.Context, typeID int64 )  (*domain.GoodsType, error) var  goodsType GoodsTypeif  res := g.data.db.First(&goodsType, typeID); res.RowsAffected == 0  {return  nil , errors.New("商品类型不存在" )return  res, nil 
data 层 specifications.go 新增方法
注意这里调用 repo 的方式,用的是 g.data.DB(ctx) 而不是之前的 g.data.db,这里是因为引入了 GORM MySQL 的事务,如果你对在 kratos 使用 GORM MySQL 的事务还不是很熟悉的话,请查看我之前写的一篇 kratos 中使用 GORM MySQL 的事务  的文章。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 func  (g *specificationRepo)  CreateSpecification (ctx context.Context, req *domain.Specification)  (int64 , error) return  s.ID, result.Errorfunc  (g *specificationRepo)  CreateSpecificationValue (ctx context.Context, AttrId int64 , req []*domain.SpecificationValue)  error var  value []*SpecificationsAttrValuefor  _, v := range  req {append (value, res)return  result.Error
测试 还是使用上一次介绍的工具,如图:
你可以少写参数或故意写错一些参数来验证,写的判断逻辑是否生效,这里就不演示了。
结束语 本篇只提供了一个商品规格参数的创建方法,其他方法没有在文章中体现,单元测试方法也没有编写,重复性的工作这里就不编写了,通过前几篇的文章,相信你可以自己完善剩余的方法。
下一篇开始编写本文中提到的商品属性,敬请期待。 
感谢您的耐心阅读,动动手指点个赞吧。
关注我获取更新