商品sku設計
sku是什麼?
是一種表示庫存進出計量的單位,例如,盒,件。如今的sku被廣泛引申為某款產品的統一編號的簡稱,每一個產品都有它獨一無二的sku號。sku號包括其商品的品牌、型號、等級、配置、單位、用途、產地、價格、生產日期、保質期等等一系列屬性,每一件商品的這些屬性與其他任何商品都不一樣,這樣的商品稱為一個單品,其sku號是獨一無二的。
從貨品角度來講,sku是指單獨的一個商品,商品所有的屬性都已經被確定。這可以這麼說,只要商品的屬性不一樣,那麼兩者的sku就不一樣。商品是屬性包括:品牌、型號、等級、配置、成分、花色、用途等等因素。只要商品的某一個屬性不同,就可以定義為不同的sku。
從業務管理的角度來講,sku也包含商品包裝的資訊。計量單位,包裝單位不一樣,其適應的管理不一樣,我們也可以分成不同的sku。打個比方,襪子,我們都是以“雙”為單位的,也就是一雙襪子是一個sku,襪子引數都一樣,只是在打包過程中,一包裡面12雙襪子,那麼12雙襪子與1雙襪子又是不同的sku。
多規格sku設計,一直挺讓我頭疼的,當然,也可能是一直沒有做的原因,這次做的時候,也想了好多,我的做法實現邏輯不是很好,只是實現了功能,小夥伴們可以做一個參考,有更好的辦法也可以分享一下。
下面開始說我的實現步驟,先貼資料表
DROP TABLE IF EXISTS `dishes`;
CREATE TABLE `dishes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shop_id` int(11) NOT NULL COMMENT '店鋪id',
`class_id` varchar(255) NOT NULL COMMENT '頂級分類id',
`sub_classid` varchar(30) NOT NULL COMMENT '商品分類id',
`keywork` varchar(255) NOT NULL COMMENT '關鍵字,以|隔開',
`shop_menu_id` varchar(255) NULL COMMENT '菜譜id',
`name` varchar(200) NOT NULL COMMENT '商品名',
`main_picture` bigint(15) NOT NULL COMMENT '商品主圖',
`picture` varchar(2000) NOT NULL COMMENT '商品圖片列表',
`explain` varchar(255) NULL COMMENT '說明',
`isspec` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否多規格,1/0',
`original_price` int(10) NOT NULL COMMENT '原價價格,100 = 1元',
`price` int(10) NOT NULL COMMENT '現價,100 = 1元',
`status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '上下架,1/0',
`cost_price` int(10) NOT NULL DEFAULT 0 COMMENT '成本價,計算利潤',
`sort` int(10) NOT NULL COMMENT '排序(僅在後臺生效)',
`sales` int(10) NOT NULL DEFAULT 0 COMMENT '銷量',
`grade` decimal(2, 1) NOT NULL DEFAULT 5.0 COMMENT '評分',
`weight` decimal(10.2) NOT NULL DEFAULT 1000.00 COMMENT '權重,新菜品權重1000,一星期後下降至500,為起始值,5星好評+1,4星不增不減,3星-0.5,1星-2,2星-1,自動評價不增不減',
`created_at` int(10) NOT NULL COMMENT '建立時間',
`updated_at` int(10) NOT NULL COMMENT '更新時間',
PRIMARY KEY (`id`),
INDEX `shop_id`(`shop_id`) USING BTREE COMMENT '店鋪id',
INDEX `shop_menu_id`(`shop_menu_id`) USING BTREE COMMENT '菜譜id'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT='菜品表';
DROP TABLE IF EXISTS `dishes_attr`;
CREATE TABLE `dishes_attr` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shop_id` int(10) NOT NULL COMMENT '店鋪id',
`dishes_id` int(10) NOT NULL COMMENT '所屬商品id',
`sort` int(10) NULL COMMENT '排序',
`name` varchar(200) NOT NULL COMMENT '屬性名',
`sku` varchar(100) NOT NULL COMMENT 'sku值',
PRIMARY KEY (`id`),
INDEX `shop_id`(`shop_id`) USING BTREE COMMENT '店鋪id',
INDEX `dishes_id`(`dishes_id`) USING BTREE COMMENT '商品id'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT='菜品屬性表';
DROP TABLE IF EXISTS `dishes_spec`;
CREATE TABLE `dishes_spec` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`dishes_id` int(10) NOT NULL COMMENT '商品id',
`attr_path` varchar(100) NOT NULL COMMENT '屬性序列化資料',
`attr_name` varchar(300) NOT NULL COMMENT '屬性名稱,以-分開',
`picture` bigint(15) NULL COMMENT '規格圖片',
`original_price` int(10) NOT NULL DEFAULT 0 COMMENT '原價',
`price` int(10) NOT NULL COMMENT '現價',
`cost_price` int(10) NOT NULL COMMENT '成本價',
PRIMARY KEY (`id`),
INDEX `dishes_id`(`dishes_id`) USING BTREE COMMENT '商品id',
INDEX `attr_path`(`attr_path`) USING BTREE COMMENT '屬性路徑'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT='商品規格表';
新增商品時提交的資料格式
前端程式碼沒有實現,我在專案裡只做後端,所以這邊只貼後端程式碼,這邊只說前端需要提交的資料格式
{
"class_id":1, // 分類id
"sub_classid":2, // 下級分類id
"shop_menu_id":4, // 菜譜id
"name":"商品名", // 商品名
"main_picture":"https://www.baidu.com", // 商品主圖
"picture":"https://www.baidu.com,https://www.baidu.com,https://www.baidu.com", // 商品其他圖片
"explain":"本商品是一份,不是 兩份,這是個說明", // 商品詳情,不建議太長
"isspec":1, // 是否多規格:1/0
"original_price":1000, // 原價,注意,如果是10.00元,則為1000
"price":500, // 現價,注意,如果是10.00元,則為1000
"status":1, // 是否上架:1/0
"cost_price":1500, // 成本價,用於統計例如
"sort":15, // 排序,僅在後臺和商品詳情使用
"attr":[
{
"name":"顏色", // 屬性1
"sku":"紅色" // 屬性值1.1
},
{
"name":"顏色", // 屬性1
"sku":"藍色" // 屬性值1.2
},
{
"name":"尺碼",
"sku":"l"
},
{
"name":"尺碼",
"sku":"xl"
},
{
"name":"款式",
"sku":2018
},
{
"name":"款式",
"sku":2019
}
],
"spec":[
{
"attr_name":"紅色+l+2018", // 屬性值拼接資料,注意順序,不可以錯誤,按照表單順序拼接
"picture":"http://www.baidu.com", // 圖片,可不傳
"original_price":"1500", // 原價
"price":"1000", // 現價
"cost_price":"500" // 成本價
},
{
"attr_name":"紅色+l+2019",
"picture":"http://www.baidu.com",
"original_price":"1500",
"price":"1000",
"cost_price":"500"
},
{
"attr_name":"紅色+xl+2018",
"picture":"http://www.baidu.com",
"original_price":"1500",
"price":"1000",
"cost_price":"500"
},
{
"attr_name":"紅色+xl+2019",
"picture":"http://www.baidu.com",
"original_price":"1500",
"price":"1000",
"cost_price":"500"
},
{
"attr_name":"藍色+l+2018",
"picture":"http://www.baidu.com",
"original_price":"1500",
"price":"1000",
"cost_price":"500"
},
{
"attr_name":"藍色+l+2019",
"picture":"http://www.baidu.com",
"original_price":"1500",
"price":"1000",
"cost_price":"500"
},
{
"attr_name":"藍色+xl+2018",
"picture":"http://www.baidu.com",
"original_price":"1500",
"price":"1000",
"cost_price":"500"
},
{
"attr_name":"藍色+xl+2019",
"picture":"http://www.baidu.com",
"original_price":"1500",
"price":"1000",
"cost_price":"500"
}
]
}
商品控制器
/**
* 建立商品
* @param Request $request [description]
* @param Dishes $dishes [description]
* @return [type] [description]
*/
public function createDishes( Request $request , Dishes $dishes ) {
// 獲取引數
$param = $request->input();
// 啟用事務
DB::beginTransaction();
$data = $dishes->store( $request );
if ( !$data ) {
return $this->fail( 30017 );
}
// 是否是多規格
if ( !$param['isspec'] ) {
// 提交非多規格商品
DB::commit();
return $this->success( $data );
} else {
// 組織新增規格屬性
$attrData = $this->attr( $param , $data );
if ( $attrData != true ) {
DB::rollBack();
return $this->fail( $attrData );
} else {
DB::commit();
return $this->success( '成功' );
}
}
}
/**
* 組織商品屬性
* @param [array] $param [新增商品的表單資料]
* @param [int] $id [商品id]
* @return [array] [屬性新增成功後從mysql裡的取值]
*/
public function attr( $param , $id ) {
if ( !is_array( $param ) || !$param ) {
return 30018;
}
$attrData = [];
$i = 1;
foreach ($param['attr'] as $key => $value) {
$attrData[] = [
'shop_id' => $this->shop_id,
'dishes_id' => $id,
'sort' => $i,
'name' => $value['name'],
'sku' => $value['sku']
];
$i++;
}
// 啟用事務
DB::beginTransaction();
// 屬性組
$attr = new Dishes_Attr();
$data = $attr->store( $attrData );
if ( !$data ) {
return 30020;
} else {
// 查詢剛剛新增的屬性
$result = $attr->selAttrs( $id );
if ( !$result ) {
return 30020;
}
$paramData = [
// 資料庫中取出的屬性引數
'attr' => $result,
// 表單資料中規格
'spec' => $param['spec'],
// 商品id
'dishes_id' => $id
];
return $this->spec( $paramData );
}
}
/**
* 新增規格
* @param [type] $data [description]
* @return [type] [description]
*/
public function spec( $data ) {
// 處理屬性從資料庫中取出的資料
$attr = $this->attrGroup( $data['attr'] );
// 拼接規格引數
$result = $this->CartesianProduct( $attr );
// 預定義規格陣列
$specData = [];
// 處理規格陣列
foreach ( $result as $value ) {
foreach ($data['spec'] as $val) {
if ( $val['attr_name'] == $value['attr_name'] ) {
// 組織規格資料
$specData[] = [
'dishes_id' => $data['dishes_id'],
'attr_name' => $value['attr_name'],
'attr_path' => $value['attr_path'],
'picture' => $val['picture'],
'original_price' => intval ( $val['original_price'] ? $val['original_price'] : 0 ),
'price' => intval( $val['price'] ),
'cost_price' => intval ( $val['cost_price'] ? $val['cost_price'] : 0 )
];
// 跳出迴圈
break;
}
}
}
if ( count( $specData ) != count( $result ) ) {
return 30019;
}
// 新增規格
$spec = new Dishes_Spec();
$insertSpec = $spec->store( $specData );
if ( $insertSpec ) {
return true;
} else {
return 30020;
}
}
/**
* 處理屬性從資料庫中取出的資料
* @param [type] $data [description]
* @return [type] [description]
*/
public function attrGroup( $data ) {
$attr = [];
$attr_spec = [];
// 處理陣列
$i = 0;
foreach ($data as $val){
if ( isset($attr[$val['name']]) ) {
continue;
}
// 定義陣列
if ( !isset($attr[$val['name']]) ) {
$attr[$val['name']] = 1;
}
foreach( $data as $value ) {
if ( $attr[$val['name']] == 1 ) {
$i++;
}
$attr[$val['name']] = 2;
if ( $val['name'] == $value['name'] ) {
$attr_spec[$i][] = [
'id' => $value['id'],
'shop_id' => $value['shop_id'],
'dishes_id' => $value['dishes_id'],
'sort' => $value['sort'],
'sku' => $value['sku']
];
} else {
continue;
}
}
}
return array_values( $attr_spec );
}
/**
* 卡迪爾演算法,拼接規格引數
* @param [type] $goods [description]
*/
public function CartesianProduct($goods)
{
$returnData = [];
for ($i=0; $i < count($goods)-1 ; $i++) {
// 初始化
if($i == 0)
{
foreach( $goods[$i] as $value1 ) {
$returnData[] = [
'attr_name' => $value1['sku'],
'attr_path' => $value1['id']
];
}
}
$tempArray = []; // 臨時陣列
foreach( $returnData as $value ) {
foreach( $goods[$i+1] as $val ) {
$tempArray[] = [
'attr_name' => $value['attr_name'].'+'.$val['sku'],
'attr_path' => $value['attr_path'].','.$val['id']
];
}
}
//重新賦值
$returnData = $tempArray;
}
return $returnData;
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結