电商最小存货单位SKU——详细代码实现

电商最小存货单位SKU——详细代码实现1、什么是SKU? 2、什么是笛卡尔积? 3、SKU组合代码实现思路 4、SKU选择代码实现思路

1、什么是SKU?

2、什么是笛卡尔积?

3、SKU组合代码实现思路

4、SKU选择代码实现思路

什么是SKU?

SKU(Stock Keeping Unit)最小存货单位。

例如我们在某购物APP上选购一款Mac电脑,可以选择颜色、内存、以及其他的一些销售属性。

这些销售属性的组合,比如深空灰16G+512G官方标配版,就可以作为这个商品库存及发货的标准,也就是我们说的一个SKU 电商最小存货单位SKU——详细代码实现 如果你明白了SKU的概念,那接下来看看这个例子,这个商品一共有几个SKU? 电商最小存货单位SKU——详细代码实现


我是一条分割线


一共是10个SKU,你答对了么?

根据这个商品的尺码和颜色,我们可以得到10种[尺码X颜色]的组合,分别是

[XS黑色,XS军绿色,S黑色,S军绿色,M黑色,M军绿色,L黑色,L军绿色,XL黑色,XL军绿色]

这些SKU组合,我们可以通过笛卡尔积来得到。那么什么是笛卡尔积?

笛卡尔积

在数学中,笛卡尔积是一种对集合的运算。

假设有集合A, 集合B,用A中的元素作为第一个元素,用B中的元素作为第二个元素构成的有序对,所有这样的有序对的集合叫做A和B的笛卡尔积,记作AxB

用符号来表示记为:A×B={(x,y)|x∈A∧y∈B}

例如:A={a, b}, B={1,2,3}

用集合A中的a作为第一个元素,集合B中的1作为第二个元素,得到(a,1)

用集合A中的a作为第一个元素,集合B中的2作为第二个元素,得到(a,2)

用集合A中的a作为第一个元素,集合B中的3作为第二个元素,得到(a,3)

用集合A中的b作为第一个元素,集合B中的1作为第二个元素,得到(b,1)

用集合A中的b作为第一个元素,集合B中的2作为第二个元素,得到(b,2)

用集合A中的b作为第一个元素,集合B中的3作为第二个元素,得到(b,3)

最后我们得到AxB={(a,1),(a,2),(a,3),(b,1),(b,2),(b,3)}

是不是和我们上面计算SKU组合是一个意思呢?

SKU组合代码实现思路

现在基本的概念和组合方法我们都已经Get了,看下如何转变成代码,让计算机帮我们实现笛卡尔积

就以一个实际的例子来引入吧

现在 你是一个服装电商卖家,要上架一款大衣,这款大衣颜色有黑色,白色,灰色;长度有长款,短款;尺码有S,M,L,那么可能的SKU组合我们通过树状图画出来

电商最小存货单位SKU——详细代码实现

我们先来用最简单粗暴的循环实现看看

const colors = ["黑色", "白色", "灰色"];
const length = ["长款", "短款"];
const size = ["S", "M", "L"];

// 获取SKU组合的方法
const getSkuList = () => {
  const result = [];
  colors.forEach((c) => {
    length.forEach((l) => {
      size.forEach((s) => {
        result.push([c, l, s]);
      });
    });
  });
  return result; // [["黑色","长款","S"],["黑色","长款","M"],["黑色","长款","L"],["黑色","短款","S"],["黑色","短款","M"],["黑色","短款","L"],["白色","长款","S"],["白色","长款","M"],["白色","长款","L"],["白色","短款","S"],["白色","短款","M"],["白色","短款","L"],["灰色","长款","S"],["灰色","长款","M"],["灰色","长款","L"],["灰色","短款","S"],["灰色","短款","M"],["灰色","短款","L"]]
};

确实结果是没问题的,但这种暴力循环没法应用到实际业务当中去。如果一个商品有10个销售属性,总不能写10层循环吧。况且我们会有各种不同的商品,不同的销售属性,不能像这样在for循环里把循环的数组定义死。

我们可以考虑借助reduce函数,将数组的每一项进行拼接操作,最终整合成一个结果

const colors = ["黑色", "白色", "灰色"];
const length = ["长款", "短款"];
const size = ["S", "M", "L"];

// 方法一
const getSkuList = (attrList) => {
  if (attrList.length < 2) return attrList[0] || [];
  return attrList.reduce((total, current) => {
    const res = [];
    total.forEach((t) => {
      current.forEach((c) => {
        const temp = Array.isArray(t) ? [...t] : [t];
        temp.push(c);
        res.push(temp);
      });
    });
    return res;
  });
}

// 方法二
const getSkuList2 = (attrList) => {
  return attrList.reduce(
    (total, current) => total.flatMap((t) => current.map((c) => [...t, c])),
    [[]]
  );
};


getSkuList([colors,length,size]); // [["黑色","长款","S"],["黑色","长款","M"],["黑色","长款","L"],["黑色","短款","S"],["黑色","短款","M"],["黑色","短款","L"],["白色","长款","S"],["白色","长款","M"],["白色","长款","L"],["白色","短款","S"],["白色","短款","M"],["白色","短款","L"],["灰色","长款","S"],["灰色","长款","M"],["灰色","长款","L"],["灰色","短款","S"],["灰色","短款","M"],["灰色","短款","L"]]

从树状图我们能看出来,从上往下到叶子结点的每一条路径,代表着一个SKU组合。其实还有点类似经典的回溯算法组合问题。

那我们再尝试使用回溯算法来实现一下。回溯算法的基本思想我在这里就不多做解释了,感兴趣的同学可以自行了解。

对于我们这个场景来说,回溯的“终止条件”就是当路径长度等于销售属性个数的时候,说明已经访问到叶子结点了,一个SKU组合成功,可以存放到结果中去。每一层的结点就是每一个销售属性的选项,例如第一层就是颜色,第二层是长度,第三层是尺码。我们看下代码如何实现的:

const colors = ["黑色", "白色", "灰色"];
const length = ["长款", "短款"];
const size = ["S", "M", "L"];

// 方法三
const getSkuList = (attrList) => {
  const result = [];
  const backTracking = (path, level) => {
    if (path.length === attrList.length) {
      result.push([...path]);
      return;
    }
    attrList[level].forEach((item) => {
      path.push(item);
      backTracking(path, level + 1);
      path.pop();
    });
  };
  backTracking([], 0);
  return result; 
};

getSkuList([colors,length,size]); // [["黑色","长款","S"],["黑色","长款","M"],["黑色","长款","L"],["黑色","短款","S"],["黑色","短款","M"],["黑色","短款","L"],["白色","长款","S"],["白色","长款","M"],["白色","长款","L"],["白色","短款","S"],["白色","短款","M"],["白色","短款","L"],["灰色","长款","S"],["灰色","长款","M"],["灰色","长款","L"],["灰色","短款","S"],["灰色","短款","M"],["灰色","短款","L"]]

SKU选择代码实现思路

屏幕录制2022-02-15 下午2.38.22的副本.gif

前面我们实现了SKU组合的方法,主要适用于卖家后台系统,在创建商品时,编辑各SKU的信息。

在买家端,买家会选择自己需要的SKU,系统需要根据买家的选择,实时反馈对应SKU的价格、库存等信息。

服务端给到前端的信息是一个完整的SKU列表,数据结构类似这样, 数组里的每一条数据即为一个SKU,包含了SKU的唯一标识skuId, SKU的属性组成,SKU的库存和价格

[
  {
    skuId: "111",
    skuInfo: {
      color: "黑色",
      length: "长款",
      size: "S",
    },
    stock: 10,
    price: 10,
  },
  {
    skuId: "112",
    skuInfo: {
      color: "黑色",
      length: "长款",
      size: "M",
    },
    stock: 9,
    price: 9,
  },
  {
    skuId: "113",
    skuInfo: {
      color: "黑色",
      length: "长款",
      size: "L",
    },
    stock: 8,
    price: 8,
  },
  ...
]

以及一个完整的销售属性列表,数据结构类似这样:

[
  {
    label: "颜色",
    name: "color",
    options: [
      {
        value: "黑色",
      },
      {
        value: "白色",
      },
      {
        value: "灰色",
      },
    ],
  },
  {
    label: "长度",
    name: "length",
    options: [
      {
        value: "长款",
      },
      {
        value: "短款",
      },
    ],
  },
  {
    label: "尺码",
    name: "size",
    options: [
      {
        value: "S",
      },
      {
        value: "M",
      },
      {
        value: "L",
      },
    ],
  },
]

根据这个销售属性列表,我们很容易可以渲染出SKU选择的组件(Demo为React+antd实现)

<div className="main-content">
      <Form name="basic"> {attrs.map((attr) => { return ( <Form.Item key={attr.name} label={attr.label} name={attr.name}> <div className="form-content"> {attr.options.map((opt, index) => ( <Button key={index} type={ opt.value === selectedSku[attr.name] ? "primary" : "default" } className="opt" disabled={!isSkuValid(attr.name, opt.value)} onClick={() => onClick(attr.name, opt.value)} > {opt.value} </Button> ))} </div> </Form.Item> ); })} </Form>
    </div>

我们用一个对象来记录已选择的选项, 当点击选项时,更新selectedSku对象的值

  const [selectedSku, setSelectedSku] = useState({});

  const onClick = (attrKey, optValue) => {
    setSelectedSku({
      ...selectedSku,
      [attrKey]: selectedSku[attrKey] === optValue ? "" : optValue,
    });
  };

实际情况下,某些SKU存在无货的场景,那对应的按钮是置灰无法选择的。但是目前SKU组件是依靠销售属性列表渲染的,没有库存信息,也就是说,我们需要结合销售属性数组和SKU列表,去判断每一个选项是否可选。

判断SKU是否可选.png

  const isSkuValid = (attrKey, optValue) => {
    // 先假设当前属性值已选中,拼入已选对象里
    const tempSelectedSku = {
      ...selectedSku,
      [attrKey]: optValue,
    };
    // 过滤出已选对象中属性值不为空的
    const skuToBeChecked = Object.keys(tempSelectedSku).filter(
      (key) => tempSelectedSku[key] !== ""
    );

    // 在skuList里找到所有包含已选择属性的sku且库存>0的sku
    const filteredSkuList = skuList.filter((sku) =>
      skuToBeChecked.every(
        (skuKey) =>
          tempSelectedSku[skuKey] === sku.skuInfo[skuKey] && sku.stock > 0
      )
    );

    return filteredSkuList.length > 0;
  };

这样我们就实现了买家端SKU选择的功能。

总结

至此,卖家后台创建商品SKU及买家界面选择SKU的功能基本都已实现,有问题欢迎一起探讨

今天的文章电商最小存货单位SKU——详细代码实现分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15754.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注