商品sku设计
商品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 协议》,转载必须注明作者和本文链接
推荐文章: