建議使用大屏設備(例如pad/pc),能夠更好的瀏覽本篇文章前端
今天咱們開始「商品系統」的篇章。本文分爲以下五大模塊:git
第一篇咱們主要看看一個入門的電商平臺(B2C)如何去構建本身的基礎商品信息,其實這個事情很簡單,想一想咱們的現實生活,商家擺放商品到貨架,客戶從貨架挑選商品,客戶把挑選好的商品放入購物車(籃),最後客戶去收銀臺結帳。github
對於一個電商平臺來說,咱們怎麼理解上面的簡單示例呢?接着,咱們來拆分上面這個簡單的事情:web
商家擺放商品到貨架,客戶從貨架挑選商品,客戶把挑選好的商品放入購物車(籃),最後客戶去收銀臺結帳sql
備註:本篇文章主要來看看一、二、三、4步該如何去設計。json
經過上面的分析咱們能夠得出下面的信息:數據結構
整理以後獲得以下的需求點:架構
需求點 | 功能點 | 項目命名 | 技術棧 |
---|---|---|---|
商品後臺系統 | 1.建立商品 2.發佈商品到前臺系統 | Temporal Backend | PHP |
前臺系統 | 1.商品列表 2.商品詳情 | Skr Frontend | Vue |
接口網關 | 企業總線 | Skr Gateway | kong |
商品服務 | 1.建立商品接口 2.商品狀態變動接口 2.商品列表接口 3. 商品詳情接口 | Temporal Service | Golang |
經過上面的需求分析,再加上以前的《電商設計手冊之用戶體系》中的用戶體系和《支付開發,不得不瞭解的國內、國際第三方支付流程》中的支付服務,咱們規劃出如下的架構圖。app
對咱們程序猿來說「商品系統」剛開始的樣子就是以下三點:學習
很簡單是吧,基本一張表就搞定了,看起來也是沒什麼問題的。可是呢,程序設計的巧妙之處就在於抽象能力,電商行業把goods_id
進行了進一步的抽象,產生了Spu和Sku概念,在瞭解Spu和Sku定義以前,咱們還得了解下銷售屬性的含義,舉個例子便於理解:
想一想咱們的現實生活,假如咱們去批發市場上了一批AJ1球鞋,批發商會給咱們不一樣配色、大小的AJ1球鞋。咱們在店裏銷售這些商品時都會詢問客戶:「您是須要什麼顏色和大小的AJ1球鞋呢?」。這裏的顏色和大小就是所謂的銷售屬性,由於不一樣顏色和大小的AJ1球鞋可能價格不一樣、庫存數量不一樣,現實生活中是否是如此,不一樣顏色或大小的AJ1都有差異巨大的價格。
接着,咱們來看看Spu和Sku定義:
名稱 | 概念 | 解釋 |
---|---|---|
Spu | standard product unit 標準產品單位 | goods_id剝離銷售屬性的部分,例如:小米8。商品列表咱們展現Spu列表。 |
Sku | stock keeping unit 庫存量單位 | 就是你想買的那個商品真正的編號,這個編號對應的庫存就是你想買的那個商品的庫存量。Spu+一或多個銷售屬性對應一個Sku,例如:小米8黑128G,其中黑和128G就是銷售屬性,小米8就是一個Spu。 |
搞清楚了麼?
因此最後簡單的商品表就拆成了spu表和sku表,接着咱們還抽象出來了可複用的銷售屬性表和銷售屬性值表。除此以外 咱們應該還有品牌表、類別表、簡單的sku庫存表(目前簡單設計此表,後期具體業務重構此表)。接着咱們列下這些表的明細:
表名稱 | 表名 |
---|---|
品牌表 | product_brands |
類別表 | product_category |
spu表 | product_spu |
sku表 | product_sku |
銷售屬性表 | product_attr |
銷售屬性值 | product_attr_value |
sku庫存表 | product_sku_stock |
除了上面的表以外,我又加了另外一張表 關聯關係冗餘表 product_spu_sku_attr_map
,爲何呢?顧名思義,冗餘用的,有了這張表,咱們能夠很高效的的到:
具體表結構以下所示:
-- 品牌表 product_brands
CREATE TABLE `product_brands` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '品牌ID',
`name` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '品牌名稱',
`desc` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '品牌描述',
`logo_url` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '品牌logo圖片',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='品牌表';
-- 類別表 product_category
CREATE TABLE `product_category` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '分類ID',
`pid` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '父ID',
`name` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '分類名稱',
`desc` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '分類描述',
`pic_url` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '分類圖片',
`path` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '分類地址{pid}-{child_id}-...',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='類別表';
-- spu表 product_spu
-- spu: standard product unit 標準產品單位
CREATE TABLE `product_spu` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'SPU ID',
`brand_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '品牌ID',
`category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '分類ID',
`name` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT 'spu名稱',
`desc` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT 'spu描述',
`selling_point` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '賣點',
`unit` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT 'spu單位',
`banner_url` text COMMENT 'banner圖片 多個圖片逗號分隔',
`main_url` text COMMENT '商品介紹主圖 多個圖片逗號分隔',
`price_fee` int unsigned NOT NULL DEFAULT 0 COMMENT '售價,整數方式保存',
`price_scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '售價,金額對應的小數位數',
`market_price_fee` int unsigned NOT NULL DEFAULT 0 COMMENT '市場價,整數方式保存',
`market_price_scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '市場價,金額對應的小數位數',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT AUTO_INCREMENT=666666 CHARSET=utf8mb4 COMMENT='spu表';
-- sku表 product_sku
-- sku: stock keeping unit 庫存量單位
CREATE TABLE `product_sku` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'SKU ID',
`spu_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'SPU ID',
`attrs` text COMMENT '銷售屬性值{attr_value_id}-{attr_value_id} 多個銷售屬性值逗號分隔',
`banner_url` text COMMENT 'banner圖片 多個圖片逗號分隔',
`main_url` text COMMENT '商品介紹主圖 多個圖片逗號分隔',
`price_fee` int unsigned NOT NULL DEFAULT 0 COMMENT '售價,整數方式保存',
`price_scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '售價,金額對應的小數位數',
`market_price_fee` int unsigned NOT NULL DEFAULT 0 COMMENT '市場價,整數方式保存',
`market_price_scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '市場價,金額對應的小數位數',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT AUTO_INCREMENT=666666 CHARSET=utf8mb4 COMMENT='sku表';
-- 銷售屬性表 product_attr
CREATE TABLE `product_attr` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '銷售屬性ID',
`name` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '銷售屬性名稱',
`desc` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '銷售屬性描述',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='銷售屬性表';
-- 銷售屬性值 product_attr_value
CREATE TABLE `product_attr_value` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '銷售屬性值ID',
`attr_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '銷售屬性ID',
`value` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '銷售屬性值',
`desc` varchar(255) unsigned NOT NULL DEFAULT '' COMMENT '銷售屬性值描述',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='銷售屬性值';
-- 關聯關係冗餘表 product_spu_sku_attr_map
-- 1. spu下 有哪些sku
-- 2. spu下 有那些銷售屬性
-- 3. spu下 每一個銷售屬性對應的銷售屬性值(一對多)
-- 4. spu下 每一個銷售屬性值對應的sku(一對多)
CREATE TABLE `product_spu_sku_attr_map` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`spu_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'SPU ID',
`sku_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'SKU ID',
`attr_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '銷售屬性ID',
`attr_name` varchar(255) NOT NULL DEFAULT '0' COMMENT '銷售屬性名稱',
`attr_value_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '銷售屬性值ID',
`attr_value_name` varchar(255) NOT NULL DEFAULT '0' COMMENT '銷售屬性值',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='關聯關係冗餘表';
-- sku庫存表 product_sku_stock
CREATE TABLE `product_sku_stock` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`sku_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'SKU ID',
`quantity` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '庫存',
`create_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立時間',
`create_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '建立人staff_id',
`update_at` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新時間',
`update_by` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '修改人staff_id',
`status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '狀態 1:enable, 0:disable, -1:deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='sku庫存表';
複製代碼
關於接口設計目前很簡單,無非列表和詳情。可是這裏我作了一個很好的設計動靜分離,例如庫存的動態的數據,單獨提供接口,其餘列表和詳情數據徹底靜態化,把流量打到CDN去,這裏又會說到咱們下步計劃的基礎服務體系裏的「靜態資源服務」,這個服務的主要功能就是把咱們的接口數據靜態化。具體的V1.0版的接口設計以下:
一、spu詳情 GET {version}/product/spu/{spu_id}
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
spu_id | number | yes | spu ID |
響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"brand_info": {
"id": "number, 品牌ID",
"name": "string, 品牌名稱",
"desc": "string, 品牌描述",
"logo_url": "string, 品牌logo圖片",
},
"category_info": {
"id": "number, 分類ID",
"name": "string, 品牌名稱",
"desc": "string, 品牌描述",
"pic_url": "string, 分類圖片",
"path": "string, 分類地址{pid}-{child_id}-...",
},
"spu_info": {
"id": "number, spu id",
"name": "string, spu名稱",
"desc": "string, spu描述",
"selling_point": "string, 賣點",
"unit": "string, spu單位",
"banner_url": [
"string, banner 圖片url",
"string, banner 圖片url",
],
"main_url": [
"string, 商品介紹主圖 圖片url",
"string, 商品介紹主圖 圖片url",
],
"price": "string, 售價",
"market_price": "string, 市場價",
"attrs": [ // 有那些銷售屬性
{
"id": "銷售屬性ID",
"name": "string, 銷售屬性名稱",
"desc": "string, 銷售屬性描述",
"values": [ // 每一個銷售屬性對應的銷售屬性值(一對多)
{
"id": "銷售屬性值ID",
"name": "string, 銷售屬性值",
"desc": "string, 銷售屬性值描述",
// 每一個銷售屬性值對應的sku(一對多)
// 頁面初始化時,按鈕不可點擊邏輯判斷: 若是該銷售屬性值下全部sku沒有庫存,則該銷售屬性按鈕不可點擊
// 選擇銷售屬性值時,按鈕不可點擊邏輯判斷:銷售屬性構成雙向鏈表,每一個銷售屬性又是一個單向鏈表存改銷售屬性對應的全部銷售屬性值。每當選擇一個銷售屬性值時先前和後一個銷售屬性遍歷,執銷售屬性值下全部sku售罄的按鈕不可點擊,且當前銷售屬性值map記錄key爲當前點擊的銷售屬性值ID,值統一標示一下就行,目的記錄是因爲選擇了哪一個銷售屬性值使得當前的銷售屬性值爲售罄狀態
// 取消選擇銷售屬性值時,按鈕不可點擊邏輯恢復判斷:數據結構同上,遍歷,記錄的map刪除key爲當前取消選中的銷售屬性值,並判斷是否還有別的key使得該銷售屬性值爲售罄狀態,若是沒有則恢復未售罄狀態
"skus": [
"number, sku id",
"number, sku id",
],
}
],
}
],
"skus": [ // 有哪些sku
"number, sku id",
"number, sku id",
],
"skus_map": {
"{attr_value_id}-{attr_value_id}-...": "number, sku id",
"{attr_value_id}-{attr_value_id}-...": "number, sku id",
"{attr_value_id}-{attr_value_id}-...": "number, sku id",
"{attr_value_id}-{attr_value_id}-...": "number, sku id",
"{attr_value_id}-{attr_value_id}-...": "number, sku id",
"{attr_value_id}-{attr_value_id}-...": "number, sku id",
}
}
}
}
複製代碼
二、獲取spu下全部skus庫存 GET {version}/stock/spu/{spu_id}
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
spu_id | number | yes | spu ID |
響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"skus_stock": {
"int, sku id": {
"quantity": "int, 剩餘庫存數量"
}
}
}
}
}
複製代碼
三、sku詳情 GET {version}/product/sku/{sku_id}
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
sku | number | yes | sku ID |
響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"id": "number, sku id",
"name": "string, sku名稱",
"desc": "string, sku描述",
"unit": "string, sku單位",
"banner_url": [
"string, banner 圖片url",
"string, banner 圖片url",
],
"main_url": [
"string, 商品介紹主圖 圖片url",
"string, 商品介紹主圖 圖片url",
],
"price": "string, 售價",
"market_price": "string, 市場價",
}
}
複製代碼
四、spu列表 GET {version}/product/spu/list
請求參數:
字段 | 類型 | 是否必傳 | 描述 |
---|---|---|---|
- | - | - | - |
響應內容:
{
"code": "200",
"msg": "OK",
"result": {
"list": [
{
"id": "number, spu id",
"name": "string, spu名稱",
"desc": "string, spu描述",
"unit": "string, spu單位",
"banner_url": [
"string, banner 圖片url",
"string, banner 圖片url",
],
"price": "string, 售價",
"market_price": "string, 市場價",
}
]
}
}
複製代碼
最後,若是有寫的不對或者不完善的地方,但願你們多多評論,互相學習互相進步~
項目地址: github.com/skr-shop/ma…
下篇文章咱們主要專一到基礎商品信息的前端交互設計,好比Spu詳情頁面多銷售屬性的選擇如何聯動等,盡情期待。