1.商品規格數據結構
樂優商城是一個全品類的電商網站,因此商品的種類繁多,每一件商品,其屬性又有差別。為了更准確描述商品及細分差別,抽象出兩個概念:SPU和SKU,了解一下:
(1)SPU和SKU
SKU:Stock Keeping Unit(庫存量單位),SPU商品集因具體特性不同而細分的每個商品
-
-
因為顏色、內存等不同,而細分出不同的Mate10,如亮黑色128G版。(SKU)
可以看出:
-
SPU是一個抽象的商品集概念,為了方便后台的管理。
-
SKU才是具體要銷售的商品,每一個SKU的價格、庫存可能會不一樣,用戶購買的是SKU而不是SPU
(2)規格參數表
<1>表結構
我們看下規格參數的格式:
因此我們設計了兩張表:
-
tb_spec_group:組,與商品分類關聯
-
<2>規格組
規格參數分組表:tb_spec_group
CREATE TABLE `tb_spec_group` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `cid` bigint(20) NOT NULL COMMENT '商品分類id,一個分類下有多個規格組', `name` varchar(50) NOT NULL COMMENT '規格組的名稱', PRIMARY KEY (`id`), KEY `key_category` (`cid`) ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='規格參數的分組表,每個商品分類下有多個規格參數組';
-
-
cid:商品分類id,一個分類下有多個模板
-
<3>規格參數
規格參數表:tb_spec_param
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(255) NOT NULL COMMENT '參數名', `numeric` tinyint(1) NOT NULL COMMENT '是否是數字類型參數,true或false', `unit` varchar(255) DEFAULT '' COMMENT '數字類型參數的單位,非數字類型可以為空', `generic` tinyint(1) NOT NULL COMMENT '是否是sku通用屬性,true或false', `searching` tinyint(1) NOT NULL COMMENT '是否用於搜索過濾,true或false', `segments` varchar(1000) DEFAULT '' COMMENT '數值類型參數,如果需要搜索,則添加分段間隔值,如CPU頻率間隔:0.5-1.0', PRIMARY KEY (`id`), KEY `key_group` (`group_id`), KEY `key_category` (`cid`) ) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COMMENT='規格參數組下的參數名';
通用屬性:
用一個布爾類型字段來標記是否為通用:
-
generic來標記是否為通用屬性:
-
true:代表通用屬性
-
false:代表sku特有屬性
-
搜索過濾:
與搜索相關的有兩個字段:
-
searching:標記是否用作過濾
-
true:用於過濾搜索
-
false:不用於過濾
-
-
segments:某些數值類型的參數,在搜索時需要按區間划分,這里提前確定好划分區間
-
比如電池容量,0~2000mAh,2000mAh~3000mAh,3000mAh~4000mAh
-
數據類型:
某些規格參數可能為數值類型,這樣的數據才需要划分區間,我們有兩個字段來描述:
-
numberic:是否為數值類型
-
true:數值類型
-
false:不是數值類型
-
-
(3)表關系總結:
2.商品規格參數管理
(1)頁面布局
<1>整體布局
打開規格參數頁面,看到如下內容:
因為規格是跟商品分類綁定的,因此首先會展現商品分類樹,並且提示你要選擇商品分類,才能看到規格參數的模板。一起了解下頁面的實現:
頁面結構(Specification.vue):
可以看出頁面分成2個部分:
-
<v-flex xs3>
:左側,內部又分上下兩部分:商品分類樹及標題-
v-card-title
:標題部分,這里是提示信息,告訴用戶要先選擇分類,才能看到模板 -
v-tree
:這里用到的是我們之前講過的樹組件,展示商品分類樹,
-
-
<v-flex xs9 class="px-1">
(2)規格組的查詢
<1>樹節點的點擊事件
-
記錄當前選中的節點,選中的就是商品分類
-
showGroup
被置為true,則規格組就會顯示了。
同時,我們把被選中的節點(商品分類)的id傳遞給了SpecGroup
<2>頁面查詢規格組
我們查看頁面控制台,可以看到請求已經發出:
<3>后端代碼
最終代碼截圖:
實體類:
service業務代碼:
(1)實體類:SpecGroup
package lucky.leyou.item.domain; import javax.persistence.*; import java.util.List; @Table(name = "tb_spec_group") public class SpecGroup { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long cid; private String name; @Transient private List<SpecParam> params; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getCid() { return cid; } public void setCid(Long cid) { this.cid = cid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<SpecParam> getParams() { return params; } public void setParams(List<SpecParam> params) { this.params = params; } }
實體類:SpecParam
package lucky.leyou.item.domain; import javax.persistence.*; @Table(name = "tb_spec_param") public class SpecParam { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private Long cid; private Long groupId; private String name; @Column(name = "`numeric`") private Boolean numeric; private String unit; private Boolean generic; private Boolean searching; private String segments; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getCid() { return cid; } public void setCid(Long cid) { this.cid = cid; } public Long getGroupId() { return groupId; } public void setGroupId(Long groupId) { this.groupId = groupId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Boolean getNumeric() { return numeric; } public void setNumeric(Boolean numeric) { this.numeric = numeric; } public String getUnit() { return unit; } public void setUnit(String unit) { this.unit = unit; } public Boolean getGeneric() { return generic; } public void setGeneric(Boolean generic) { this.generic = generic; } public Boolean getSearching() { return searching; } public void setSearching(Boolean searching) { this.searching = searching; } public String getSegments() { return segments; } public void setSegments(String segments) { this.segments = segments; } }
<1>mapper
SpecParamMapper
package lucky.leyou.item.mapper; import lucky.leyou.item.domain.SpecParam; import tk.mybatis.mapper.common.Mapper; public interface SpecParamMapper extends Mapper<SpecParam> { }
SpecGroupMapper
package lucky.leyou.item.mapper; import lucky.leyou.item.domain.SpecGroup; import tk.mybatis.mapper.common.Mapper; public interface SpecGroupMapper extends Mapper<SpecGroup> { }
<2>controller
package lucky.leyou.item.controller; import lucky.leyou.item.domain.SpecGroup; import lucky.leyou.item.service.ISpecificationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; @Controller @RequestMapping(path = "/spec") public class SpecificationController { @Autowired private ISpecificationService specificationService; /** * 根據分類id查詢分組 * @param cid * @return */ @GetMapping(path = "/groups/{cid}") public ResponseEntity<List<SpecGroup>> queryGroupsByCid(@PathVariable("cid")Long cid){ List<SpecGroup> groups = this.specificationService.queryGroupsByCid(cid); if (CollectionUtils.isEmpty(groups)){ return ResponseEntity.notFound().build(); } return ResponseEntity.ok(groups); } }
<3>service
接口
package lucky.leyou.item.service; import lucky.leyou.item.domain.SpecGroup; import java.util.List; public interface ISpecificationService { /** * 根據分類id查詢分組 * @param cid * @return */ public List<SpecGroup> queryGroupsByCid(Long cid); }
實現類
package lucky.leyou.item.service.impl; import lucky.leyou.item.domain.SpecGroup; import lucky.leyou.item.mapper.SpecGroupMapper; import lucky.leyou.item.mapper.SpecParamMapper; import lucky.leyou.item.service.ISpecificationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class SpecificationServiceImpl implements ISpecificationService { @Autowired private SpecGroupMapper specGroupMapper; @Autowired private SpecParamMapper specParamMapper; /** * 根據分類id查詢分組 * @param cid * @return */ @Override public List<SpecGroup> queryGroupsByCid(Long cid) { SpecGroup specGroup = new SpecGroup(); specGroup.setCid(cid); return this.specGroupMapper.select(specGroup); } }
<4>效果圖
(3)規格參數查詢
<1>表格切換
當我們點擊規格組,會切換到規格參數顯示,肯定是在規格組中綁定了點擊事件:
我們看下事件處理:
可以看到這里是使用了父子通信,子組件觸發了select事件:
再來看下父組件的事件綁定:
事件處理:
並且,我們把group也傳遞到spec-param
<2>頁面查詢規格參數
查看頁面控制台,發現請求已經發出:
<3>后台代碼
(1)在SpecificationController類中添加如下方法
/** * 根據條件查詢規格參數 * @param gid * @return */ @GetMapping(path = "/params") public ResponseEntity<List<SpecParam>> queryParams(@RequestParam("gid")Long gid){ List<SpecParam> params = this.specificationService.queryParams(gid); if (CollectionUtils.isEmpty(params)){ return ResponseEntity.notFound().build(); } return ResponseEntity.ok(params); }
(2)在ISpecificationService及其實現類中添加如下方法
/** * 根據條件查詢規格參數 * @param gid * @return */ @Override public List<SpecParam> queryParams(Long gid) { SpecParam param = new SpecParam(); param.setGroupId(gid); return this.specParamMapper.select(param); }
<4>重啟微服務
<5>效果圖