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