商品sku設計

mjc123456發表於2020-08-21

商品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 協議》,轉載必須註明作者和本文連結
馬江川@13753441707

相關文章