乐优商城(04)--商品规格
樂優商城(04)–商品規格
一、商品規格數據結構
樂優商城是一個全品類的電商網站,因此商品的種類繁多,每一件商品,其屬性又有差別。為了更準確描述商品及細分差別,抽象出兩個概念:SPU和SKU,了解一下:
1.1.SPU和SKU
SPU:Standard Product Unit (標準產品單位) ,一組具有共同屬性的商品集
SKU:Stock Keeping Unit(庫存量單位),SPU商品集因具體特性不同而細分的每個商品
可以看出:
- SPU是一個抽象的商品集概念,為了方便后臺的管理。
- SKU才是具體要銷售的商品,每一個SKU的價格、庫存可能會不一樣,用戶購買的是SKU而不是SPU
1.2、數據庫表設計分析
數據庫中關于商品規格的表有五個即:
tb_spu表分析
以上面華為手機商品為例,頭部會顯示分類和品牌,所以需要該商品所屬的三個級別的分類id以及品牌id,
每個商品都會有標題和副標題,SPU是一個抽象的商品集概念,是為了方便后臺的管理的,所以需要該商品的上下架的狀態修改,以及創建時間和最后修改時間
這里是做了表的垂直拆分,將SPU的詳情放到了另一張表:tb_spu_detail
tb_spu_detail這張表中的數據都比較大,為了不影響主表的查詢效率所以拆分出這張表。
tb_spu_detail表分析
所以包含的字段有
description:描述 specification:規格 packaging_list:包裝 after_service:售后服務 comment:評價評價的數據量很龐大且數據價值很低,若存儲在mysql中性能很不好,后面使用mongoDB存儲
一個分類下的所有SPU具有類似的規格參數。SPU下的SKU可能會有不同的規格參數信息,因此:
- SPUDetail中保存通用的規格參數信息。
- SKU中保存特有規格參數。
需要注意的是這兩個字段:generic_spec和special_spec。
-
generic_spec字段
其中保存通用規格參數信息的值,為了方便查詢,使用json格式
- key:對應的規格參數的spec_param的id
- value:對應規格參數的值
-
special_spec字段
以手機為例,品牌、操作系統等肯定是全局通用屬性,內存、顏色等肯定是特有屬性。
當確定了一個SPU,比如小米的:小米11
全局屬性值都是固定的了:
品牌:小米 型號:小米11特有屬性舉例:
顏色:[香檳金, 櫻花粉, 磨砂黑] 內存:[6G, 8G] 機身存儲:[56GB, 128GB, 256GB]顏色、內存、機身存儲,作為SKU特有屬性,key雖然一樣,但是SPU下的每一個SKU,其值都不一樣,所以值會有很多,形成數組。
在SPU中,會把特有屬性的所有值都記錄下來,形成一個數組,也是json結構:
- key:規格參數id
- value:spu屬性的數組
之所以在spu中也記錄一份特有規格參數,是因為有時候需要把所有規格參數都查詢出來,而不是只查詢1個sku的屬性。比如,商品詳情頁展示可選的規格參數時
tb_sku表分析
不同的商品分類,屬性是不同,比如手機有內存,衣服有尺碼,所以很難設計在同一張表中
但是注意SKU是具體的商品信息,也是有一些單個屬性的
即:與spu關聯、標題、產品圖片、價格、特有屬性
SKU的特有屬性是商品規格參數的一部分:
這樣規格參數中的屬性可以標記成兩部分:
- spu下所有sku共享的規格屬性(稱為全局屬性)
- 每個sku不同的規格屬性(稱為特有屬性)
查看搜索面板:
不難發現很多過濾條件在規格參數中都能找到,規格參數中的數據,將來會有一部分作為搜索條件來使用。所以在設計時,將這部分屬性標記出來,將來做搜索的時候,作為過濾條件。要注意的是,無論是SPU的全局屬性,還是SKU的特有屬性,都有可能作為搜索過濾條件的,并不沖突,而是有一個交集:
-
indexes字段
保存的是特有屬性待選項的下標組合,中間用’_'連接
拿上面的產品圖為例:
選中了:羽墨黑,4GB+128GB,官方標配,優惠套餐2
indexes字段內容為:0_0_0_1
這樣設計當用戶點擊選中一個特有屬性,就能根據角標快速定位到sku。
tb_spec_group
可以看到規格參數是分組的,每一組都有多個參數鍵值對。不過對于規格參數的模板而言,其值現在是不確定的,不同的商品值肯定不同,模板中只要保存組信息、組內參數信息即可。
因此設計了兩張表:
- tb_spec_group:組,與商品分類關聯
- tb_spec_param:參數名,與組關聯,一對多
該tb_spec_group表中有三個字段:
- id:主鍵
- cid:商品分類id,一個分類下有多個模板
- name:該規格組的名稱。
tb_spec_param
-
通用屬性
用一個布爾類型字段來標記是否為通用:
- generic來標記是否為通用屬性:
- true:代表通用屬性
- false:代表sku特有屬性
- generic來標記是否為通用屬性:
-
搜索過濾
與搜索相關的有兩個字段:
- searching:標記是否用作過濾
- true:用于過濾搜索
- false:不用于過濾
- segments:某些數值類型的參數,在搜索時需要按區間劃分,這里提前確定好劃分區間
- 比如電池容量,02000mAh,2000mAh3000mAh,3000mAh~4000mAh
- searching:標記是否用作過濾
-
數值類型
某些規格參數可能為數值類型,這樣的數據才需要劃分區間,我們有兩個字段來描述:
- numberic:是否為數值類型
- true:數值類型
- false:不是數值類型
- unit:參數的單位
- numberic:是否為數值類型
二、商品規格管理
2.1、頁面布局
打開規格參數頁面,看到如下內容:
因為規格是跟商品分類綁定的,因此首先會展現商品分類樹,并且提示你要選擇商品分類,才能看到規格參數的模板。一起了解下頁面的實現:
點擊該頁面,可以發現這里使用了v-layout來完成頁面布局,并且添加了row屬性,代表接下來的內容是行布局(左右)。
可以看出頁面分成2個部分:
- <v-flex xs3>:左側,內部又分上下兩部分:商品分類樹及標題
- v-card-title:標題部分,這里是提示信息,告訴用戶要先選擇分類,才能看到模板
- v-tree:這里用到的是我們之前講過的樹組件,展示商品分類樹,
- <v-flex xs9 class="px-1">:右側:內部是規格參數展示
2.2、右側規格
右側規格最終效果:
可以看到右側分為上下兩部分:
- 上部:面包屑,顯示當前選中的分類
- 下部:table,顯示規格參數信息
頁面實現:
這是一個spec-group組件(規格組)和spec-param組件(規格參數),這是提前定義的獨立組件。在SpecGroup中定義了表格。
2.3、規格組的查詢
2.3.1、前端頁面
當點擊樹節點時,要將v-dialog打開,因此必須綁定一個點擊事件:(Specification.vue)
來看下handleClick方法:(Specification.vue)
點擊事件發生時,發生了兩件事:
- 記錄當前選中的節點,選中的就是商品分類
- showGroup被置為true,則規格組就會顯示了。
同時,把被選中的節點(商品分類)的id傳遞給了SpecGroup組件:(Specification.vue)
來看下SpecGroup.vue中的實現:
查看頁面控制臺,可以看到請求已經發出:
2.3.1、后端實現
實體類
在leyou-item-interface中添加實體類:
@Table(name = "tb_spec_param") public class SpecParam {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id; //主鍵private Long cid; //商品分類idprivate Long groupId; //分組idprivate String name; //參數名@Column(name = "`numeric`") //該字段在數據庫中為關鍵字,需特殊處理private Boolean numeric; //是否是數字類型參數private String unit; //數字類型參數的單位private Boolean generic; //是否是通用參數private Boolean searching; //是否可用于查詢private String segments; //若數值類型為搜素,需添加一個數字范圍//get和set方法 } @Table(name = "tb_spec_group") public class SpecGroup {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id; //主鍵private Long cid; //分類id 一個分類下會有多個規格組private String name; //規格組的名稱@Transientprivate List<SpecParam> params; //參數集合//get和set方法 }mapper
/*** SpecGroup 通用mapper*/ public interface SpecGroupMapper extends Mapper<SpecGroup> { }這里將SpecGroup和SpecParam兩個放在一起實現
controller
- 請求方式:get
- 請求路徑:/spec/groups/{cid} ,這里通過路徑占位符傳遞商品分類的id
- 請求參數:商品分類id
- 返回結果:頁面是直接把resp.data賦值給了groups:
service
public interface SpecificationService {/*** 根據分類id查詢參數組* @param cid* @return*/List<SpecGroup> queryGroupsByCid(Long cid); }實現類:
@Service public class SpecificationServiceImpl implements SpecificationService {@Autowiredprivate SpecGroupMapper specGroupMapper;/*** 根據分類id查詢參數組** @param cid* @return*/@Overridepublic List<SpecGroup> queryGroupsByCid(Long cid) {SpecGroup specGroup = new SpecGroup();specGroup.setCid(cid);return this.specGroupMapper.select(specGroup);} }2.4、規格參數查詢
2.4.1、前端頁面
表格切換:
當點擊規格組,會切換到規格參數顯示,肯定是在規格組中綁定了點擊事件:
看下事件處理:
可以看到這里是使用了父子通信,子組件觸發了select事件.
再來看下父組件的事件綁定:
事件處理:
這里記錄了選中的分組,并且把標記設置為false,這樣規格組就不顯示了,而是顯示:SpecParam
并且,把group也傳遞到spec-param組件:
來看SpecParam.vue的實現:
查看頁面控制臺,發現請求已經發出:
2.4.2、后端實現
mapper
/*** SpecParam 的通用mapper*/ public interface SpecParamMapper extends Mapper<SpecParam> { }controller
- 請求方式:GET
- 請求路徑:/spec/params
- 請求參數:gid,分組id
- 返回結果:該分組下的規格參數集合List<SpecParam>
service
/*** 根據gid查詢具體參數* @param gid* @return*/ List<SpecParam> queryParamsByGid(Long gid);實現類:
@Autowired private SpecParamMapper specParamMapper;/*** 根據gid查詢具體參數** @param gid* @return*/ @Override public List<SpecParam> queryParamsByGid(Long gid) {SpecParam specParam = new SpecParam();specParam.setGroupId(gid);return this.specParamMapper.select(specParam); }2.5、規格參數組增、刪、改
2.5.1、前端頁面
點擊新增分組按鈕,請求已發:
查看前端頁面代碼:
2.5.2、后端實現
增加:
controller
- 請求方式:POST
- 請求路徑:/item/spec/group
- 請求參數:specGroup
- 返回結果:無,響應狀態碼為201
service
/*** 添加一個參數分組* @param specGroup* @return*/ void addSpecGroup(SpecGroup specGroup);實現類:
/*** 添加一個參數分組** @param specGroup* @return*/ @Override public void addSpecGroup(SpecGroup specGroup) {this.specGroupMapper.insert(specGroup); }修改:
controller
- 請求方式:PUT
- 請求路徑:/item/spec/group
- 請求參數:specGroup
- 返回結果:無,響應狀態碼為202
service
/*** 修改一個參數分組* @param specGroup* @return*/ void updateSpecGroup(SpecGroup specGroup);實現類
/*** 修改一個參數分組** @param specGroup* @return*/ @Override public void updateSpecGroup(SpecGroup specGroup) {this.specGroupMapper.updateByPrimaryKeySelective(specGroup); }刪除:
controller
- 請求方式:POST
- 請求路徑:/item/spec/group
- 請求參數:id,即參數組的id
- 返回結果:無,響應狀態碼為200
service
/*** 根據組id刪除參數組* @param gid* @return*/ void deleteSpecGroup(Long gid);實現類:
/*** 根據組id刪除參數組** @param gid* @return*/ @Override @Transactional public void deleteSpecGroup(Long gid) {//先查詢該參數組內的參數List<SpecParam> specParams = queryParamsByGid(gid);//如果組內有數據if (specParams.size() > 0){//刪除組內的具體參數specParams.forEach(specParam -> this.specParamMapper.deleteByPrimaryKey(specParam));}//再刪除參數組this.specGroupMapper.deleteByPrimaryKey(gid); }2.6、規格參數的增、刪、改
2.6.1、前端頁面
點即新增參數,查看表單和發送的請求
查看前端頁面代碼:
2.6.2、后端實現
新增:
controller
- 請求方式:POST
- 請求路徑:/item/spec/group
- 請求參數:SpecParam
- 返回結果:無,響應狀態碼為201
service
/*** 添加一個參數* @param specParam* @return*/ void addSpecParam(SpecParam specParam);實現類:
/*** 添加一個參數** @param specParam* @return*/ @Override public void addSpecParam(SpecParam specParam) {this.specParamMapper.insert(specParam); }修改:
controller
- 請求方式:PUT
- 請求路徑:/item/spec/group
- 請求參數:SpecParam
- 返回結果:無,響應狀態碼為202
service
/*** 修改一個參數* @param specParam* @return*/ void updateSpecParam(SpecParam specParam);實現類:
/*** 修改一個參數** @param specParam* @return*/ @Override public void updateSpecParam(SpecParam specParam) {this.specParamMapper.updateByPrimaryKeySelective(specParam); }刪除:
controller
- 請求方式:DELETE
- 請求路徑:/item/spec/group
- 請求參數:id,即參數的id
- 返回結果:無,響應狀態碼為202
service
/*** 根據參數的id刪除參數* @param id* @return*/ void deleteSpecParam(Long id);實現類:
/*** 根據參數的id刪除參數** @param id* @return*/ @Override public void deleteSpecParam(Long id) {this.specParamMapper.deleteByPrimaryKey(id); }總結
以上是生活随笔為你收集整理的乐优商城(04)--商品规格的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java nextintln_java
- 下一篇: 算法 --- 希尔排序、归并排序、快速