day09

第一种设计

解决方案是,采用json来保存整个规格参数模板,不需要额外的表,一个字符串就够了

以主芯片这一组为例:

    group:注明,这里是主芯片

    params:该组的所有规格属性,因为不止一个,所以是一个数组。这里包含四个规格属性:
    CPU品牌,CPU型号,CPU频率,CPU核数。每个规格属性都是一个对象,包含以下信息:

k:属性名称
    searchable:是否作为搜索字段,将来在搜索页面使用,boolean类型
    global:是否是SPU全局属性,boolean类型。true为全局属性,false为SKU的特有属性
    options:属性值的可选项,数组结构。起约束作用,不允许填写可选项以外的值,比如CPU核数,有人添10000核岂不是很扯淡
    numerical:是否为数值,boolean类型,true则为数值,false则不是。为空也代表非数值
    unit:单位,如:克,毫米。如果是数值类型,那么就需要有单位,否则可以不填。
总结下:

    规格参数分组,每组有多个参数
    参数的 k代表属性名称,没有值,具体的SPU才能确定值
    参数会有不同的属性:是否可搜索,是否是全局、是否是数值,这些都用boolean值进行标记:
    SPU下的多个SKU共享的参数称为全局属性,用global标记
    SPU下的多个SKU特有的参数称为特有属性
    如果参数是数值类型,用numerical标记,并且指定单位unit
    如果参数可搜索,用searchable标记
第二种设计

如果按照传统数据库设计,这里至少需要3张表:

    spec_group:代表组,与商品分类关联
    spec_param:属性名,与组关联商品分类,一对多
    param_value==>spu表中:属性备选值,与属性名关联,一对多
这样程序的复杂度大大增加,但是提高了数据的复用性。

把商品的图片存到虚拟机
数据库的image属性值http://image.ayh.com/images/…
在nginx的image.ayh.com配置加  (static目录下有image文件夹)

location / {

            root   /home/ayh/static;

            index  index.html index.htm;

        }

商品规格数据结构
一个商品分类下有多个规格参数组,一个规格参数组下有多个规格参数,分成2个表
规格参数组表(和商品分类表绑定)
CREATE TABLE `tb_spec_group` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',

  `cid` bigint(20) NOT NULL COMMENT '商品分类id,一个分类下有多个规格组',

  `name` varchar(32) NOT NULL COMMENT '规格组的名称',

  PRIMARY KEY (`id`),

  KEY `key_category` (`cid`)

) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COMMENT='规格参数的分组表,每个商品分类下有多个规格参数组';

@Table(name = "tb_spec_group")
@Data
public class SpecGroup {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private String name;
    private Long cid;
}

规格参数表(和商品表绑定)
CREATE TABLE `tb_spec_param` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',

  `cid` bigint(20) NOT NULL COMMENT '商品分类id',

  `group_id` bigint(20) NOT NULL,

  `name` varchar(256) NOT NULL COMMENT '参数名',

  `numeric` tinyint(1) NOT NULL COMMENT '是否是数字类型参数,true或false',

  `unit` varchar(256) DEFAULT '' COMMENT '数字类型参数的单位,非数字类型可以为空',

  `generic` tinyint(1) NOT NULL COMMENT '是否是sku通用属性,true或false',

  `searching` tinyint(1) NOT NULL COMMENT '是否用于搜索过滤,true或false',

  `segments` varchar(1024) DEFAULT '' COMMENT '数值类型参数,如果需要搜索,则添加分段间隔值,如CPU频率间隔:0.5-1.0',

  PRIMARY KEY (`id`),

  KEY `key_group` (`group_id`),

  KEY `key_category` (`cid`)

) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8 COMMENT='规格参数组下的参数名';

@Table(name = "tb_spec_param")
@Data
public class SpecParam {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private Long gid;
    private String name;
    private Boolean numeric;//是否为数值类型
    private String unit;//数值单位
    private Boolean generic;//是否通用
    private Boolean searching;//是否可搜索
    private String segments;//数值类型参数,如果需要搜索,则添加分段间隔值
}

商品的表结构
商品抽象出两个概念
SPU:Standard Product Unit (标准产品单位) ,一组具有共同属性的商品集

SKU:Stock Keeping Unit(库存量单位),SPU商品集因具体特性不同而细分的每个商品

SPU是一个抽象的商品集概念,为了方便后台的管理。
SKU才是具体要销售的商品,每一个SKU的价格、库存可能会不一样,用户购买的是SKU而不是SPU
SPU表
表结构
CREATE TABLE `tb_spu` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'spu id',

  `name` varchar(128) NOT NULL DEFAULT '' COMMENT '名称',

  `sub_title` varchar(256) DEFAULT '' COMMENT '子标题',

  `cid1` bigint(20) NOT NULL COMMENT '1级类目id',

  `cid2` bigint(20) NOT NULL COMMENT '2级类目id',

  `cid3` bigint(20) NOT NULL COMMENT '3级类目id',

  `brand_id` bigint(20) NOT NULL COMMENT '商品所属品牌id',

  `saleable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否上架,0下架,1上架',

  `valid` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0已删除,1有效',

  `create_time` datetime DEFAULT NULL COMMENT '添加时间',

  `last_update_time` datetime DEFAULT NULL COMMENT '最后修改时间',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=195 DEFAULT CHARSET=utf8 COMMENT='spu表,该表描述的是一个抽象性的商品,比如 iphone8';

把spu表做了垂直拆分,将SPU的详情放到了另一张表:tb_spu_detail
表中的数据都比较大,为了不影响主表的查询效率我们拆分出这张表。

CREATE TABLE `tb_spu_detail` (

  `spu_id` bigint(20) NOT NULL,

  `description` text COMMENT '商品描述信息',

  `generic_spec` varchar(3000) NOT NULL DEFAULT '' COMMENT '通用规格参数数据',

  `special_spec` varchar(1000) NOT NULL COMMENT '特有规格参数及可选值信息,json格式',

  `packing_list` varchar(1000) DEFAULT '' COMMENT '包装清单',

  `after_service` varchar(1000) DEFAULT '' COMMENT '售后服务',

  PRIMARY KEY (`spu_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

规格参数与商品分类绑定,一个分类下的所有SPU具有类似的规格参数。SPU下的SKU可能会有不同的规格参数.
SPU中保存通用的规格参数信息。
SKU中保存特有规格参数。
generic_spec,其中保存通用规格参数信息的值,这里为了方便查询,使用了json格式,
json结构,其中都是键值对:

     key:对应的规格参数的spec_param的id
     value:对应规格参数的值

special_spec,在SPU中,会把特有属性的所有值都记录下来,形成一个数组,
也是json结构:

      key:规格参数id
      value:spu属性的数组
SKU表
表结构
CREATE TABLE `tb_sku` (

  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'sku id',

  `spu_id` bigint(20) NOT NULL COMMENT 'spu id',

  `title` varchar(255) NOT NULL COMMENT '商品标题',

  `images` varchar(1000) DEFAULT '' COMMENT '商品的图片,多个图片以‘,’分割',

  `price` bigint(15) NOT NULL DEFAULT '0' COMMENT '销售价格,单位为分',

  `indexes` varchar(100) COMMENT '特有规格属性在spu属性模板中的对应下标组合',

  `own_spec` varchar(1000) COMMENT 'sku的特有规格参数,json格式',

  `enable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否有效,0无效,1有效',

  `create_time` datetime NOT NULL COMMENT '添加时间',

  `last_update_time` datetime NOT NULL COMMENT '最后修改时间',

  PRIMARY KEY (`id`),

  KEY `key_spu_id` (`spu_id`) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='sku表,该表表示具体的商品实体,如黑色的64GB的iphone 8';

indexes,保存特有规格参数的值的下标。在SPU表中,已经对特有规格参数及可选项进行了保存。
每一个属性值,对应于SPUoptions数组的一个选项,我们记录下角标,将不同角标串联起来,作为SPU下不同SKU的标示。这就是我们的indexes字段。

own_spec,保存的是特有属性的键值对
SPU中保存的是可选项,但不确定具体的值,而SKU中的保存的就是具体的值。

还有一张表,代表库存:因为库存字段写频率较高,而SKU的其它字段以读为主,因此我们将两张表分离,读写不会干扰
CREATE TABLE `tb_stock` (

  `sku_id` bigint(20) NOT NULL COMMENT '库存对应的商品sku id',

  `seckill_stock` int(9) DEFAULT '0' COMMENT '可秒杀库存',

  `seckill_total` int(9) DEFAULT '0' COMMENT '秒杀总数量',

  `stock` int(9) NOT NULL COMMENT '库存数量',

  PRIMARY KEY (`sku_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='库存表,代表库存,秒杀库存等信息';

 代码
pojo,添加了2个字段,页面发送请求的时候要用的,数据库里面没有的字段,应该写一个vo类,然后使用的时候和po类相互转换。这里省略了。

@Data
@Table(name = "tb_spu")
public class SPU {
    @Id
    @KeySql(useGeneratedKeys = true)
    private Long id;
    private String name;
    private String subTitle;
    private Long bid;
    private Long cid1;
    private Long cid2;
    private Long cid3;
    private Boolean saleable;//是否上下架
    private Date createTime;

    //*******以下为省去vo和po的转换********
    @JsonIgnore//页面忽略字段
    private Boolean valid;//判断是否已经删除,用于逻辑删除
    @JsonIgnore //页面忽略字段
    private Date lastUpdateTime;//最后更新时间

    @Transient //数据库没有,暂时用于页面,import javax.persistence.Transient
    private String bname;
    @Transient //数据库没有,暂时用于页面
    private String cname;

}

//controller
@RestController
@RequestMapping("spu")
public class GoodsController {
    @Autowired
    private GoodsService goodsService;
    @GetMapping("page")
    public ResponseEntity<PageResult<SPU>> queryPageInfo(
            @RequestParam( value = "page",defaultValue = "1")Integer page,
            @RequestParam( value = "rows",defaultValue = "5")Integer rows,
            @RequestParam( value = "key",required = false)String key,
            @RequestParam( value = "saleable",required = false)Boolean saleable){
        PageResult<SPU> pageResult = goodsService.queryPageInfo(page, rows, key, saleable);
        if (pageResult == null || pageResult.getCurrentPageItems().size() == 0) {
            throw new AyhException(ExceptionEnum.GOODS_TABLE_DATA_NOT_FOUND);
        }
        return ResponseEntity.ok(pageResult);
    }


@Service
public class GoodsService {
    @Autowired
    private SPUMapper spuMapper;
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private BrandService brandService;

    public PageResult<SPU> queryPageInfo(Integer page, Integer rows, String key, Boolean saleable) {
        PageHelper.startPage(page, rows);
        //过滤添加条件
        Example example = new Example(SPU.class);
        Example.Criteria criteria = example.createCriteria();
        if (StringUtils.isNotBlank(key)) {
            criteria.andLike("name","%"+key+"%");
        }
        if (saleable != null) {
            criteria.andEqualTo("saleable", saleable);
        }
        //按更新时间排序
        example.setOrderByClause("last_update_time DESC");
        List<SPU> spus = spuMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(spus)) {
            throw new AyhException(ExceptionEnum.GOODS_TABLE_DATA_NOT_FOUND);
        }
         spus = loadCategoryNameAndBrandName(spus);
        PageInfo<SPU> pageInfo = PageInfo.of(spus);
        return new PageResult<SPU>(pageInfo.getTotal(),pageInfo.getList());
    }

    private List<SPU> loadCategoryNameAndBrandName(List<SPU> spus) {
        for (SPU spu : spus) {
            //设置商品分类name
            List<Category> categories = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
            List<String> stringList = categories.stream().map(Category::getName).collect(Collectors.toList());
            spu.setCname(StringUtils.join(stringList, "/"));
            //设置品牌name
            spu.setBname(brandService.queryBrandById(spu.getBid()).getName());
        }
        return spus;
    }
}

在categoryService
/**

     * 根据分类id的集合 List<>ids 查询分类对象

     * @param ids

     * @return

     */

    public List<Category> queryByIds(List<Long> ids) {

        List<Category> list = categoryMapper.selectByIdList(ids);

        if (CollectionUtils.isEmpty(list)) {

            throw new AyhException(ExceptionEnum.CATEGORY_DATA_NOT_FOUND);

        }

        return list;

    }
//mapper 继承通用Mapper和IdListMapper
public interface CategoryMapper extends Mapper<Category>, IdListMapper<Category,Long> {

 
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sh_c_1314

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

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

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

打赏作者

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

抵扣说明:

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

余额充值