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> {

 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章